【问题标题】:How can you clone a WPF object?如何克隆 WPF 对象?
【发布时间】:2010-09-07 03:32:47
【问题描述】:

谁有一个很好的例子如何深度克隆 WPF 对象,保留数据绑定?


标记的答案是第一部分。

第二部分是您必须创建一个 ExpressionConverter 并将其注入到序列化过程中。详细信息在这里:
http://www.codeproject.com/KB/WPF/xamlwriterandbinding.aspx?fid=1428301&df=90&mpp=25&noise=3&sort=Position&view=Quick&select=2801571

【问题讨论】:

    标签: c# wpf binding clone


    【解决方案1】:

    怎么样:

        public static T DeepClone<T>(T from)
        {
            using (MemoryStream s = new MemoryStream())
            {
                BinaryFormatter f = new BinaryFormatter();
                f.Serialize(s, from);
                s.Position = 0;
                object clone = f.Deserialize(s);
    
                return (T)clone;
            }
        }
    

    当然,这个深度克隆任何对象,它可能不是城里最快的解决方案,但它的维护最少......:)

    【讨论】:

      【解决方案2】:

      我所做的最简单的方法是使用 XamlWriter 将 WPF 对象保存为字符串。 Save 方法将在逻辑树中序列化对象及其所有子对象。现在您可以创建一个新对象并使用 XamlReader 加载它。

      例如: 将对象写入 xaml(假设该对象是一个 Grid 控件):

      string gridXaml = XamlWriter.Save(myGrid);
      

      将其加载到新对象中:

      StringReader stringReader = new StringReader(gridXaml);
      XmlReader xmlReader = XmlReader.Create(stringReader);
      Grid newGrid = (Grid)XamlReader.Load(xmlReader);
      

      【讨论】:

      • 请记住,如果要放置的 UI 元素是相同的根容器,它还会克隆使它们的使用复杂化的名称。
      • 我认为这不会保留动画,是吗?
      • 明确地说,这只是解决方案的一半(因为它在 08 中出现了)。这将导致评估绑定并序列化结果。如果您希望保留绑定(如问题所问),您必须在运行时将 ExpressionConverter 添加到 Binding 类型(有关相关链接,请参阅我的问题的第二部分)或在下面查看我自己的答案以了解如何在 4.0 中执行此操作.
      • 惊人的完美:)
      • 只是可惜 XamlWriter 太慢了。希望有替代方案。
      【解决方案3】:

      在 .NET 4.0 中,新的 xaml 序列化堆栈使这变得更加容易。

      var sb = new StringBuilder();
      var writer = XmlWriter.Create(sb, new XmlWriterSettings
      {
          Indent = true,
          ConformanceLevel = ConformanceLevel.Fragment,
          OmitXmlDeclaration = true,
          NamespaceHandling = NamespaceHandling.OmitDuplicates, 
      });
      var mgr = new XamlDesignerSerializationManager(writer);
      
      // HERE BE MAGIC!!!
      mgr.XamlWriterMode = XamlWriterMode.Expression;
      // THERE WERE MAGIC!!!
      
      System.Windows.Markup.XamlWriter.Save(this, mgr);
      return sb.ToString();
      

      【讨论】:

      • 我看不出这与仅使用 XamlWriter.Save 有何不同?至少我在尝试序列化 DataGrid 时没有看到任何不同的结果。
      • @JP 抱歉,这还不是很清楚......问题是如何克隆同时保留绑定。标记的答案是正确的一半;事实上,如果你这样做,你会发现你的绑定将被评估并且结果(不是绑定本身)将被序列化。在我的问题中,我在解决方案中添加了后半部分,即添加一个 ExpressionConverter 并在运行时将其添加到 Binding 类型中。它有点晦涩难懂。这个答案可以完成同样的事情——见 HERE BE MAGIC 评论?这指示序列化程序 not 在序列化时评估绑定。整洁。
      • 是的,我看到了。我仍在用我的 Datagrid 解决同样的问题,并且没有注意到关于数据绑定的任何不同结果。我的数据网格仍然是空白的。我一定错过了别的东西。尽管如此,我会为你指出这一切而投赞成票。
      • @JPrichardson:序列化为文本并检查结果以查看是否保留了{Binding}s。
      • 我推销它很有用,我正在编写一个动态数据输入表单,这解决了我遇到的绑定问题。谢谢。
      【解决方案4】:

      这里有一些很好的答案。非常有帮助。我尝试了各种方法来复制 Binding 信息,包括 http://pjlcon.wordpress.com/2011/01/14/change-a-wpf-binding-from-sync-to-async-programatically/ 中概述的方法,但这里的信息是互联网上最好的!

      我创建了一个可重用的扩展方法来处理 InvalidOperationException “绑定在使用后无法更改。”在我的场景中,我正在维护某人编写的一些代码,在一次重大的 DevExpress DXGrid 框架升级之后,它不再起作用。以下完美解决了我的问题。我返回对象的代码部分可能会更好,我稍后会重新考虑。

      /// <summary>
      /// Extension methods for the WPF Binding class.
      /// </summary>
      public static class BindingExtensions
      {
          public static BindingBase CloneViaXamlSerialization(this BindingBase binding)
          {
              var sb = new StringBuilder();
              var writer = XmlWriter.Create(sb, new XmlWriterSettings
              {
                  Indent = true,
                  ConformanceLevel = ConformanceLevel.Fragment,
                  OmitXmlDeclaration = true,
                  NamespaceHandling = NamespaceHandling.OmitDuplicates,
              });
              var mgr = new XamlDesignerSerializationManager(writer);
      
              // HERE BE MAGIC!!!
              mgr.XamlWriterMode = XamlWriterMode.Expression;
              // THERE WERE MAGIC!!!
      
              System.Windows.Markup.XamlWriter.Save(binding, mgr);
              StringReader stringReader = new StringReader(sb.ToString());
              XmlReader xmlReader = XmlReader.Create(stringReader);
              object newBinding = (object)XamlReader.Load(xmlReader);
              if (newBinding == null)
              {
                  throw new ArgumentNullException("Binding could not be cloned via Xaml Serialization Stack.");
              }
      
              if (newBinding is Binding)
              {
                  return (Binding)newBinding;
              }
              else if (newBinding is MultiBinding)
              {
                  return (MultiBinding)newBinding;
              }
              else if (newBinding is PriorityBinding)
              {
                  return (PriorityBinding)newBinding;
              }
              else
              {
                  throw new InvalidOperationException("Binding could not be cast.");
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-08
        • 2012-11-17
        • 2012-12-07
        • 1970-01-01
        • 1970-01-01
        • 2011-11-07
        • 2017-05-22
        • 1970-01-01
        相关资源
        最近更新 更多