Tuesday, November 11, 2008

Dynamically adding XAML strings from C#

In designing the screen for my silverlight game Switcheroo, I wanted to be able to dynamically add XAML chunks that represent the pieces (Ellipse). True, I could have just created the graphic elements property by property, but since the only difference between all the game pieces is if they are black or red, I thought that if it only took one or two lines of code, it would be a more elegant solution.

I wanted to separate the internal representation of the gameboard in the gaming object from the XAML front end. Then I could just clear the gameboard and recreate all the pieces after the gameboard object was updated. The question was, how?

Well I googled high and low but though I found many "solutions" to my problem, none of them worked. After trying different combinations of the aforementioned solutions, I got closer. This is my first try that made any headway:

const string red =
"<Ellipse Stroke='Black' Fill='Red' Height='30' Width='30' Margin='10,10,10,10' />";

LayoutRoot.Children.Add((Ellipse)XamlReader.Load(red));


But the error I got back was:

AG_E_PARSER_MISSING_DEFAULT_NAMESPACE [Line: 0 Position: 0]

Not a very helpful error message! Well, googling this error I found others that were getting the same error and a simple answer - the element must contain the same namespace. The only namespace is the one at the beginning of the XAML document thought. Well, I thought, it's worth a shot. So I added the namespace lines from the beginning of the element. Our code now looked like this:

const string red =
"<Ellipse xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' Stroke='Black' Fill='Red' Height='30' Width='30' Margin='10,10,10,10' />";

LayoutRoot.Children.Add((Ellipse)XamlReader.Load(red));


And low and behold! It worked!

So when you have lots of repetitious XAML or elements like named colors that cannot be easily replicated in code, just use the XAML string!

3 comments:

Anonymous said...

Any reason you would want to use this approach instead of a standard control with a style defined in xaml attached ?

Lee Saunders said...

Absolutely! When you need the power to programmatically / dynamically create an entity. You can either do it in code, or you can build the XAML string. Some things are just plain easier to create as a XAML string, with much less code.

Frank R. Haugen said...

Does not work for me but i tweaked it to:

string newEllipse = "<Ellipse ></Ellipse>";
XmlReader reader = XmlReader.Create(new StringReader(newEllipse));
LayoutRoot.Children.Add((Ellipse)XamlReader.Load(reader));

Now it works fine!