【问题标题】:How to clone UIElement in WinRT XAML C#?如何在 WinRT XAML C# 中克隆 UIElement?
【发布时间】:2013-03-22 06:55:25
【问题描述】:

我首先尝试过这种方法,但出现错误“元素已经是另一个元素的子元素”

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

然后我检查了thisthis,但是 XamlWriter 和 XamlReader 在 WinRT 中不可用。我曾尝试使用MemberwiseClone(),但它会引发异常,“无法使用已与其底层RCW 分离的COM 对象。System.Runtime.InteropServices.InvalidComObjectException”。那么谁能告诉我如何将画布中现有的 UserControl 克隆到自身?

【问题讨论】:

  • 你想通过克隆 UIElements 解决什么问题?可能有更好的方法。

标签: c# xaml exception windows-runtime uielement


【解决方案1】:

我编写了一个 UIElement 扩展来复制元素的属性和子元素 - 请注意,它为克隆设置事件。

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

如果您觉得此代码有用,请随意使用。

【讨论】:

    【解决方案2】:

    您可以尝试使用 XamlWriter 和 XamlReader 以外的序列化程序来实现链接中描述的相同效果。例如,使用 ServiceStack.Text 将您的对象 JSON 序列化为字符串,然后从该字符串中获取一个新对象并将其添加到父对象。

    【讨论】:

    • 我试过 Json.Net,因为 ServiceStack.Text 在 WinRT 中不可用。不幸的是,它在序列化时抛出异常。当我使用JsonConvert.SerializeObject(this) 时,它会抛出StackOverflowException,当我使用JsonConvert.SerializeObjectAsync(this) 时,它会抛出JsonSerializationException(从'Callisto.Controls.WatermarkTextBox' 上的'Watermark' 获取值时出错)请注意我的用户控件使用其他用户控件Callisto 之类的工具包
    • 那么我建议根本不要克隆 MyImageControl。如果您想要实现的效果是,例如,在画布上放置两个相互重复的笑脸,则添加一个新创建的 MyImageControl,并将新控件的 imagesource(或任何适当的)属性设置为与第一的。如果您说要手动复制的属性太多,那么有些方法使用反射来循环可用属性并复制它们的值。例如,请参阅stackoverflow.com/questions/1198886/…
    • 上述解决方案在 WinRT 中也不起作用,因为 WinRT 中对 System.Reflection 的方法支持有限,例如 Type.GetFields() 在 WinRT 中不可用。
    • 最后我设法编写了CloneHelper 类,但输出不达标。我会尽快更新问题并提供更多详细信息,感谢 Boluc 的帮助。
    • UIElement 是一个引用类型,因此您的代码仅相当于 ((Canvas)this.Parent).Children.Add (this) — 我希望您能看到这不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-12
    • 1970-01-01
    相关资源
    最近更新 更多