2

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!
  }
}
Matten
  • 17,365
  • 2
  • 42
  • 64
  • So, manual cloning cannot work at all, or it just got unwieldy? Does the private state not correspond to public state? If it just got unwieldy, I suggest using reflection-based code generation. Otherwise, is it an option to duplicate their code to fix any of these shortcomings? Is the library open-source? If so, you could submit patches to make the objects clonable. – Merlyn Morgan-Graham Jul 20 '11 at 18:48
  • @Merlyn Morgan-Graham : i added a code example for my manual cloning – Matten Jul 20 '11 at 18:58

3 Answers3

6

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;
    }
Jettero
  • 133
  • 1
  • 8
3

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;
}
Community
  • 1
  • 1
Ed Bayiates
  • 11,060
  • 4
  • 43
  • 62
  • I've updated my answer. There isn't a generic way to clone things, but I've given more info. – Ed Bayiates Jul 20 '11 at 19:06
  • thanks. But point 1) is giving me the problems, my clone functions aren't working as desired :) I don't know how to call the `base.clone()` properly. – Matten Jul 20 '11 at 19:07
  • That won't help you unless you are trying to create an object of the base type. Is that what you are trying to do? If so I edited my answer to include a link to calling private methods. – Ed Bayiates Jul 20 '11 at 19:10
  • No, I don't want to create an object of the base type, but when the derived class should be cloned, it has somehow to call the base class' clone method to make sure the private fields of the base class are cloned as well :/ – Matten Jul 20 '11 at 19:15
  • If a class' Clone is written incorrectly, calling the base class Clone won't help at all. – Ed Bayiates Jul 20 '11 at 19:16
  • If you are writing the Clone function, and want to call a private function Clone in a base class, use reflection as described in my answer (this link: http://stackoverflow.com/questions/135443/how-do-i-use-reflection-to-invoke-a-private-method-in-c) – Ed Bayiates Jul 20 '11 at 19:21
  • The clone function in the base class isn't private, its public. My problem is the chaining of the clone invocations. If I'd use reflection for cloning, could I implement it in the base class and this would work even for private fields of derived classes? – Matten Jul 20 '11 at 19:24
  • @Matten let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/1676/discussion-between-aresavatar-and-matten) – Ed Bayiates Jul 20 '11 at 19:25
0

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

Eran
  • 387,369
  • 54
  • 702
  • 768