Cloning WPF controls and object hierarchies

2019-07-17 18:07发布

问题:

I have some problems cloning an object hierarchie. It's a toolkit for modelling applications, the toolbox contains class instances as prototypes. But I'm having a hard time cloning these :)

The following code shows the problem:

public abstract class Shape {
  protected List<UIElement> elements;
  private Canvas canvas;
  ...
  public Canvas getCanvas() { ... };
}

public class MovableShape : Shape {
  protected ... propertyA;
  private ... propertyXY;
  ...
}

public abstract class AbstractLayout : MovableShape, ... {
  ...
}

public class SomeLayoutClass : AbstractLayout, ... {
  ...
}

public class AContainingClass {
  SomeLayoutClass Layout { get; set; }
  ...
}

When I insert an object of AContainingClass into my project worksheet, it should be cloned. So far I tried manual cloning (which fails because of the private fields in the base classes) and binary serialization (BinaryFormatter and MemoryStreams).

The first approach lacks a way to call the base.clone() method (or am I wrong?), the latter does not work because UIElements aren't [Serializable].

Note: it must be a deep copy!

Any ideas? Thanks!


UPDATE

Just to clarify my manual cloning approach: If each class has it's own Clone method, how to call the Clone method of the base class?

public class Shape { // not abstract any more
  ...
  public Shape Clone() {
    Shape clone = new Shape() { PropertyA = this.PropertyA, ... };
    ...do some XamlWriter things to clone UIElements...
    return clone;
  }
}

public class MovableShape : Shape {
  ...
  public MovableShape Clone() {
     // how to call base.Clone??? 
     // this would be required because I have no access to the private fields!
  }
}

回答1:

If you are attempting to clone UIElements, use the XamlWriter to save to a string, though no method of cloning is foolproof. You then use XamlReader to load a copy. You could still run into issues. Things like handlers not being copied, x:Name being duplicated, etc. But for simple elements like Grid or Brush, it works great.

Edit 1: There is no generic way to clone anything, however:

1) If a clone function is written correctly, it will call the base Clone for you, and copy the base class stuff, too. If it isn't written correctly calling the base Clone won't help much, though you can call a private method this way.

2) If you have to, you can use Reflection to copy pretty much anything, even click handlers, from an old object to a new object.

3) XamlWriter and XamlReader will copy and instantiate heirarchies of objects, just minus certain properties.

Edit 2: Here's a common cloning design pattern I use in my class hierarchies. Assume base class shape, derived class circle:

protected Circle(Circle t)
{
    CopyFrom(t);
}

public override object Clone()
{
    return new Circle(this);
}

protected void CopyFrom(Circle t)
{
    // Ensure we have something to copy which is also not a self-reference
    if (t == null || object.ReferenceEquals(t, this))
        return;

    // Base
    base.CopyFrom((Shape)t);

    // Derived
    Diameter = t.Diameter;
}


回答2:

And here the function for it:

    public T XamlClone<T>(T source)
    {
        string savedObject = System.Windows.Markup.XamlWriter.Save(source);

        // Load the XamlObject
        StringReader stringReader = new StringReader(savedObject);
        System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(stringReader);
        T target = (T)System.Windows.Markup.XamlReader.Load(xmlReader);

        return target;
    }


回答3:

Haven't tried it myself, but XamlWriter looks promising.