2013-03-22 22 views
6

Próbowałem tego podejścia pierwszy ale coraz błąd „Element jest już dzieckiem innego elementu”Jak klonować UIElement w WinRT XAML C#?

var objClone = new MyImageControl(); 
objClone = this; 
((Canvas)this.Parent).Children.Add(objClone); 

Potem sprawdził this i this, ale XamlWriter i XamlReader nie jest dostępny w WinRT. Próbowałem użyć MemberwiseClone(), ale zgłasza wyjątek: "Obiekt COM, który został oddzielony od jego podstawowej RCW, nie może być użyty: System.Runtime.InteropServices.InvalidComObjectException". Czy ktoś może mi powiedzieć, w jaki sposób mogę sklonować istniejącą UserControl w moim kanwie do siebie?

+2

Jaki problem próbujesz rozwiązać, klonując UIElements? Być może jest lepszy sposób na zrobienie tego. –

Odpowiedz

1

Możesz spróbować serializerów innych niż XamlWriter i XamlReader, aby uzyskać ten sam efekt opisany przez twoje linki. Na przykład użyj ServiceStack.Text do JSON serializacji obiektu do łańcucha znaków, następnie pobierz nowy obiekt z tego łańcucha i dodaj go do elementu nadrzędnego.

+0

Próbowałem Json.Net jako ServiceStack.Text nie jest dostępny w WinRT. Niestety, podczas serializacji rzuca wyjątek. Kiedy użyłem 'JsonConvert.SerializeObject (this)' to wyrzuca 'StackOverflowException' i kiedy użyłem' JsonConvert.SerializeObjectAsync (this) 'to wyrzuca' JsonSerializationException' (Błąd otrzymywania wartości od 'Watermark' na 'Callisto.Controls.WatermarkTextBox') proszę zauważyć, że moje kontrole użytkownika używają innych formantów użytkownika z zestawu narzędzi jak [Callisto] (https://github.com/timheuer/callisto) – Xyroid

+1

Wtedy sugerowałbym nie klonowanie MyImageControl w ogóle. Jeśli efekt, który chcesz osiągnąć, to na przykład umieszczenie dwóch uśmieszków na płótnie, które są duplikatami, dodaj nowo utworzoną funkcję MyImageControl i ustaw właściwość imagesource (lub inną odpowiednią) właściwość nowej kontrolki identyczną z pierwszy. Jeśli powiesz, że istnieje zbyt wiele właściwości do kopiowania ręcznego, istnieją metody, które używają odbicia do przechodzenia przez dostępne właściwości i kopiowania ich wartości. Na przykład, zobacz http://stackoverflow.com/questions/1198886/c-sharp-using-reflection-to-copy-base-class-properties –

+0

Powyższe rozwiązanie również nie działa w WinRT, ponieważ jest ograniczona obsługa metod w WinRT dla [System.Reflection] (http://msdn.microsoft.com/en-us/library/42892f65.aspx) jak 'Type.GetFields()' nie jest dostępny w WinRT. – Xyroid

2

Napisałem rozszerzenie UIElement, które kopiuje właściwości i elementy podrzędne elementu - pamiętaj, że robi ono , a nie konfiguruje zdarzenia dla klonu.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using Windows.UI.Xaml; 
using System.Reflection; 
using Windows.UI.Xaml.Controls; 

namespace UIElementClone 
{ 
    public static class UIElementExtensions 
    { 
     public static T DeepClone<T>(this T source) where T : UIElement 
     { 

      T result; 

      // Get the type 
      Type type = source.GetType(); 

      // Create an instance 
      result = Activator.CreateInstance(type) as T; 

      CopyProperties<T>(source, result, type); 

      DeepCopyChildren<T>(source, result); 

      return result; 
     } 

     private static void DeepCopyChildren<T>(T source, T result) where T : UIElement 
     { 
      // Deep copy children. 
      Panel sourcePanel = source as Panel; 
      if (sourcePanel != null) 
      { 
       Panel resultPanel = result as Panel; 
       if (resultPanel != null) 
       { 
        foreach (UIElement child in sourcePanel.Children) 
        { 
         // RECURSION! 
         UIElement childClone = DeepClone(child); 
         resultPanel.Children.Add(childClone); 
        } 
       } 
      } 
     } 

     private static void CopyProperties<T>(T source, T result, Type type) where T : UIElement 
     { 
      // Copy all properties. 

      IEnumerable<PropertyInfo> properties = type.GetRuntimeProperties(); 

      foreach (var property in properties) 
      { 
       if (property.Name != "Name") // do not copy names or we cannot add the clone to the same parent as the original. 
       { 
        if ((property.CanWrite) && (property.CanRead)) 
        { 
         object sourceProperty = property.GetValue(source); 

         UIElement element = sourceProperty as UIElement; 
         if (element != null) 
         { 
          UIElement propertyClone = element.DeepClone(); 
          property.SetValue(result, propertyClone); 
         } 
         else 
         { 
          try 
          { 
           property.SetValue(result, sourceProperty); 
          } 
          catch (Exception ex) 
          { 
           System.Diagnostics.Debug.WriteLine(ex); 
          } 
         } 
        } 
       } 
      } 
     }   
    } 
} 

Zapraszam do korzystania z tego kodu, jeśli uznasz to za przydatne.

Powiązane problemy