复制xaml子元素

本文关键字:元素 xaml 复制 | 更新日期: 2023-09-27 18:15:28

我在一个windows手机应用程序上工作。我想复制一个画布的孩子到另一个画布。我可以用下面的代码做到这一点,但问题是我必须先从一个画布上删除它。代码:

private void add_template_Click(object sender, RoutedEventArgs e)
{
    var childrenList = Template_canvas1.Children.Cast<UIElement>().ToArray();
    root.Children.Clear();
    foreach (var c in childrenList)
    {
        Template_canvas1.Children.Remove(c);
        root.Children.Add(c);
    }
}

我想在两个画布上都保留这些元素。还有别的办法吗?

复制xaml子元素

而不是试图将相同的Template_canvas1.Children添加到root画布,首先复制那些Children,然后将副本添加到root画布。

public static T CloneXaml<T>(T source)
{
    string xaml = XamlWriter.Save(source);
    StringReader sr = new StringReader(xaml);
    XmlReader xr = XmlReader.Create(sr);
    return (T)XamlReader.Load(xr);
}

然后将循环改为:

foreach (var c in childrenList)
{
    var copy = CloneXaml(c);
    root.Children.Add(copy);
}

我还没有测试过这段代码,所以你可能需要稍微修改一下,但它应该会让你在正确的方向。

或者,你可以使用下面的代码,它是从Herbie博士的回答中复制来的:

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);
              }
            }
          }
        }
      }
    }
  }
} 

如果这些都不适合你,恐怕你必须实现你自己的序列化器。看起来David Poll实现了一个不错的serlizer,所以看看吧。使用这个序列化器就像使用XamlWriter一样简单,然后您可以使用XamlReader:

public static T CloneXaml<T>(T source)
{
    UiXamlSerializer uxs = new UiXamlSerializer();
    string xaml = uxs.Serialize(source);
    StringReader sr = new StringReader(xaml);
    XmlReader xr = XmlReader.Create(sr);
    return (T)XamlReader.Load(xr);
}

要获得这个功能,下载他的Slab库,转到"Binaries"文件夹并复制所有以"Slab . utilities . xaml"开头的dll。序列化器"到您的项目。可能需要其他一些dll作为依赖项。如果你喜欢看代码并学习,他在库中有示例解决方案。

如果没有一个好的最小化的、完整的和可验证的例子来清楚地显示你已经尝试了什么,并精确地描述你到底想要实现什么,那么就不可能确切地知道什么是最好的答案。

也就是说,考虑到WPF已经知道如何通过使用数据模板在某种意义上"克隆"元素,你的问题对我来说确实听起来很像XY问题。也就是说,你只你需要克隆已经在你的想象(可视化树的元素,而事实上你应该做的是定义一个视图模型表示的数据显示元素(s)是"克隆",定义一个数据模板,使用XAML来描述的视觉元素在视图中显示的数据模型,然后简单地应用模板是必要的,无论你想要的视觉元素是"克隆"。

。它们不会被克隆。相反,WPF会按照您的要求自动填充一个全新的视觉元素子树。由于模板允许您完全定义所有方面,因此不存在诸如尝试将事件订阅连接起来,正确设置绑定等相关问题。

在您的特定示例中(尽管它很模糊),听起来您最有可能想使用ItemsControl元素,其中ItemsPanelCanvas对象。然后,您将定义一个DataTemplate,它表示ItemsPanel中的单个项;该模板可以通过设置其DataType属性来隐式引用,也可以通过设置ItemsControl.ItemTemplate属性来显式引用。然后,当您需要数据的可视化副本时,只需创建一个ItemsControl,而不是克隆任何内容。

在用户反馈Windows Phone不能工作后的新答案

完整的最终版Windows Phone应用程序可在此下载。

有一些API的差异,例如,代替pinfo.SetMethod属性,我们必须使用pinfo.GetSetMethod()等。

其次,我在不知不觉中没有检查Name属性,该属性不能被复制,否则我们将制作另一个具有相同名称的实例。

第三,我发布了简单的情况下,简单的控件,如Button, TextBox, Rectangle等不包含孩子。如果是这种情况,就必须使用递归深度克隆来克隆子节点。因为孩子可以有更多的孩子,等等。

foreach (UIElement oldElem in Canvas1.Children)
{
    try
    {               
        Type t = oldElem.GetType();
        UIElement newElem = (UIElement)Activator.CreateInstance(t);
        PropertyInfo[] info = t.GetProperties();
        int i = 0;
        foreach (PropertyInfo pinfo in info)
        {
            if (pinfo.Name == "Name") continue;
            try
            {
                if (pinfo.GetSetMethod() != null) // avoid read-only properties
                    pinfo.SetValue(newElem, pinfo.GetValue(oldElem, null),null);
            }
            catch (Exception ex)
            {
                Debug.WriteLine((++i).ToString() + " : " + pinfo.ToString());
            }
        }
        Canvas.SetLeft(newElem, Canvas.GetLeft((oldElem)));
        Canvas.SetTop(newElem, Canvas.GetTop((oldElem)));
        Canvas2.Children.Add(newElem);
    }        
    catch (Exception ex)
    {         
    }
}

如果你想要真正的深度克隆,那么用更简单的代码替换上面的外层try块中的代码:

    foreach (UIElement oldElem in Canvas1.Children)
    {
        try
        {
            UIElement newElem = oldElem.DeepClone();
            Canvas2.Children.Add(newElem);
            Canvas.SetLeft(newElem, Canvas.GetLeft(oldElem));
            Canvas.SetTop(newElem, Canvas.GetTop(oldElem));                    
        }                
        catch (Exception ex){ }
    }

仅基于WPF的旧答案

不知道windows phone,但在WPF中,这会创建一个新的元素,并将其放在另一个画布的完全相同的位置。检查一下是否符合您的要求,否则我将重新更新。

foreach (UIElement oldElem in Canvas1.Children)
{
    Type t = oldElem.GetType();
    UIElement newElem = (UIElement)Activator.CreateInstance(t);
    PropertyInfo[] info = t.GetProperties();
    int i = 0;
    foreach (PropertyInfo pinfo in info)
    {
        try
        {
            if (pinfo.SetMethod != null) // avoid read-only properties
                pinfo.SetValue(newElem, pinfo.GetValue(oldElem));                        
        }
        catch (Exception ex)
        {
            Debug.WriteLine((++i).ToString() + " : " + pinfo.ToString());
        }
    }
    Canvas.SetLeft(newElem, Canvas.GetLeft((oldElem)));
    Canvas.SetTop(newElem, Canvas.GetTop((oldElem)));
    Canvas.SetRight(newElem, Canvas.GetRight((oldElem)));
    Canvas.SetBottom(newElem, Canvas.GetBottom((oldElem)));
    Canvas2.Children.Add(newElem);
}