Tuesday, March 4, 2008

[Silverlight] Cloning an XAML element

BLAH!!! As of the time of writing this post, there is no way to 'Clone' a XAML element in Silverlight... at least none that I can find with my poor google skills.

MY APPROACH

Ideally what I'd like to do is be able to take an element and iterate through all of it's properties and make an exact copy of each property - thus creating an exact clone [except for the name, which I'd have to change].

I currently have a recursive function that will check what type of element the object is and the redirect to the corresponding 'clone' function, which makes exact copies of specific properties for that object. This is done recursively on all of the children of that element [if children exist].

public UIElement CloneUIElement(UIElement copyMe, string instance, Canvas parent)
{
return RecurseCloneUIElement(copyMe, instance, parent);
}

public UIElement RecurseCloneUIElement(UIElement copyMe, string instance, Canvas parent)
{
UIElement temp = copyMe as UIElement;
switch (copyMe.ToString())
{
case "System.Windows.Controls.Canvas":
temp = CloneCanvas((Canvas)copyMe, instance, parent);
break;
case "System.Windows.Shapes.Rectangle":
temp = CloneRectangle((Rectangle)copyMe, instance, parent);
break;
... [All I have is canvas and rect]
}
return temp;
}

public Canvas CloneCanvas(Canvas copyMe, string instance, Canvas parent)
{
Canvas clonedCanvas = new Canvas();
clonedCanvas.SetValue(Canvas.NameProperty, copyMe.GetValue(Canvas.NameProperty) + "_" + instance);
clonedCanvas.SetValue(Canvas.LeftProperty, (double)copyMe.GetValue(Canvas.LeftProperty));
clonedCanvas.SetValue(Canvas.TopProperty, (double)copyMe.GetValue(Canvas.TopProperty));
clonedCanvas.SetValue(Canvas.WidthProperty, (double)copyMe.GetValue(Canvas.WidthProperty));
clonedCanvas.SetValue(Canvas.HeightProperty, (double)copyMe.GetValue(Canvas.HeightProperty));

// ONLY CLONING SOLID BRUSHES AT THE MOMENT.
SolidColorBrush cloneColor = copyMe.GetValue(Canvas.BackgroundProperty) as SolidColorBrush;
if (cloneColor != null) { clonedCanvas.SetValue(Canvas.BackgroundProperty, cloneColor.Color.ToString()); }

foreach (Visual child in copyMe.Children)
{
clonedCanvas.Children.Add(RecurseCloneUIElement((UIElement)child, instance, clonedCanvas));
}
return clonedCanvas;
}

FREEZABLE OBJECTS

It appears that in the 3.0 framework there are 'freezable' objects, which include brushes. Apparently there is a clone functionality with those objects, however I'm guessing it's not included in the silverlight framework since I cannot access the functionality they are talking about.


Reference Links:
How to copy Shape object through C#?
Freezable .Clone Method

4 comments:

Anonymous said...

There must be an easier way to do this.. don't you think?

yamroll said...

I have scoured the Silverlight forums and have not found a nice solution as of yet. What I have been doing now, is serializing the xaml object [again, by hard code converting properties into the xaml string] and then using XAMLReader.Load to create the 'cloned' object with the given string.

It's pretty messy as well, and requires a lot of hard coding to retrieve values [I don't want to use reflection to obtain all the properties as the xaml string would get out of hand large].

If you know of a different way, please let me know.

Anonymous said...

"How to clone objects in Silverlight without XamlWriter", look at http://blogs.microsoft.co.il/blogs/tamir/archive/2008/05/06/drawingbrush-and-deep-clone-in-silverlight.aspx,

Daniel Greitens
MVP Expression
www.maximago.de
www.yourexpression.de

yamroll said...

I was hoping to stay away from reflection, but I'll have a look at this again. Thank you for posting :)