Pergunta

Alguém tem um bom exemplo de como clonar profundamente um objeto WPF, preservando as ligações de dados?


A resposta marcada é a primeira parte.

A segunda parte é que você precisa criar um ExpressionConverter e injetá-lo no processo de serialização.Os detalhes para isso estão aqui:
http://www.codeproject.com/KB/WPF/xamlwriterandbinding.aspx?fid=1428301&df=90&mpp=25&noise=3&sort=Position&view=Quick&select=2801571

Foi útil?

Solução

A maneira mais simples de fazer isso é usar um XamlWriter para salvar o objeto WPF como uma string.O método Save serializará o objeto e todos os seus filhos na árvore lógica.Agora você pode criar um novo objeto e carregá-lo com um XamlReader.

ex:Escreva o objeto em xaml (digamos que o objeto era um controle Grid):

string gridXaml = XamlWriter.Save(myGrid);

Carregue-o em um novo objeto:

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

Outras dicas

No .NET 4.0, a nova pilha de serialização xaml torna isso MUITO mais fácil.

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

Existem algumas ótimas respostas aqui.Muito útil.Eu tentei várias abordagens para copiar informações de ligação, incluindo a abordagem descrita em http://pjlcon.wordpress.com/2011/01/14/change-a-wpf-binding-from-sync-to-async-programaticamente/ mas a informação aqui é a melhor da Internet!

Criei um método de extensão reutilizável para lidar com o InvalidoPerationException "a ligação não pode ser alterada após o uso". No meu cenário, eu estava mantendo algum código que alguém escreveu e, após uma grande atualização da estrutura do DevExpress DXGrid, ele não funcionou mais.O seguinte resolveu meu problema perfeitamente.A parte do código onde retorno o objeto poderia ser melhor, e irei refatorar isso mais tarde.

/// <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.");
        }
    }
}

Que tal:

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

É claro que isso clona profundamente qualquer objeto, e pode não ser a solução mais rápida da cidade, mas tem menos manutenção...:)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top