To XAML, with love (an experiment with XAML Serialization in Silverlight)

I’m a big fan of XAML.  It provides a nice, declarative, toolable way of defining UI, encourages separation of UI logic and application logic, and is flexible enough to allow an impressive amount of expressiveness.  In addition to being a way to describe a user interface, XAML can be used as a serialization format for arbitrary CLR objects.  A little over a month ago, as I was building out a prototype of an idea I had for a blog post for another time, I found myself looking for a way to quickly and easily serialize some data out into Isolated Storage.  I looked at a few options, such as the XML and JSON serializers in the Silverlight SDK.  Both of these work well for serialization of data, but as I was looking at them, I noticed something that failed to meet my requirements for the task at hand: these libraries are both quite large and would need to be packaged into my XAPs.  System.Xml.Serialization.dll is 314 kb, and System.Runtime.Serialization.Json.dll is 138 kb (uncompressed).  Under many circumstances in large applications, taking such dependencies might be fine, but I was looking for something small that would be acceptable to package into a quick-to-load bootstrapping application.

As a result, I thought I’d spend some time looking for another option.  It occurred to me that in WPF, I might’ve used the XamlWriter for precisely this purpose: to serialize my objects out into text.  As I thought about my options for serialization, taking assembly size into account, I found myself wondering if XAML was a good choice.  After all, Silverlight has a reader (XamlReader) built into the runtime, so I wouldn’t have to build one myself.  Perhaps that would save me the size I was looking for.  Furthermore, in Silverlight 4, the XAML parser got a major overhaul that helped ensure more consistent support of the XAML language, so I felt confident that I could produce a flexible XAML serializer.

With that in mind, I started writing.  At first, I was just hoping to build out some basic serialization into XAML – enough to suit my needs for what I was working on.  But unfortunately, once I start trying to solve a problem, I can’t leave it half-complete!  Just like that, I was hooked on the challenge of seeing how complete of a XAML serializer I could build (which helps explain my blogging absence for the last month :)).

Honestly, I expected to find a large number of issues – limitations of Silverlight that would keep me from collecting enough information to serialize to XAML properly.  The reality, however, was that I could actually get really close to full fidelity.

In the process, I learned a lot about XAML, Silverlight, and myself (a journey of self-discovery, so to speak :)).  In this post, I’ll share my results (happily included for your consumption and experimentation in my latest build of SLaB) as well as some of what I learned.  As usual, I make no promises around support or correctness in all cases.  This is sample code for your edification.  That said, if you do find an issue, please let me know, and I’ll see if I can figure out what’s going on!

POCO, oh, POCO, wherefore art thou?

I started out just trying to serialize POCOs (Plain ol’ CLR Objects).  On the surface, this is pretty straightforward – walk the object graph being serialized, writing objects and property values out using the XmlWriter.  Simple, right?  Well, there’s actually a lot going on here:

  • Walk the object graph using reflection
  • Decide whether properties are serializable (i.e. is the property read-only?  If so, is it a collection type?)
  • Retrieve TypeConverters from both properties and property types (based on the TypeConverterAttribute)
  • Determine whether to set properties as attributes (<Foo Bar=”Baz” />) or elements (<Foo><Foo.Bar><Baz /></Foo.Bar></Foo>)
  • Retrieve and honor ContentPropertyAttributes (So that if “Bar” is the ContentProperty of Foo, the example above is serialized as <Foo><Baz /></Foo>)
  • Determine whether properties should/should not be serialized based on the “ShouldSerializeXXXXX” method and the DefaultValueAttribute
  • Discover attached properties and repeat all of the above
  • Manage xml namespace definitions (e.g. xmlns:foo=”clr-namespace:MyAssembly.Foo;assembly=MyAssembly”) and scope
  • Discover/respect XmlnsDefintion and XmlnsPrefix attributes
  • Understand serialization of built-in types (e.g. Uri, string, double, int, bool, enums etc.)
  • Serialize null values (using “{x:Null}”)
  • Serialize collections
  • Serialize dictionaries (and make use of “x:Key”)
  • Properly escape strings (e.g. “{Hello, world!}” needs to get serialized as “{}{Hello World!}”)
  • Properly handle string whitespace (turning on xml:space=”preserve” at the appropriate times)
  • Avoid cycles in the object graph (I simply ignore the property if it would cause a cycle – sorry, I’m not a miracle worker!)
  • Be performant!

Nothing to it, right?  Phew, I’m tired just writing all those down!  Who knew there was so much to that XAML stuff? (answer: Rob Relyea)

Anyhow, here’s the end result of this labor of love:

POCO Serialization demo

Try changing some of the bound values (against a couple of simple test POCOs I wrote).  You’ll notice that the XAML gets printed without an issue.  XamlReader.Load() works just fine on this text, and could be used to clone objects or otherwise save them.

For posterity, here’s a quick sample of the XAML produced in the sample above:

<util:NestedObject xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                   BoolValue="True"
                   DoubleValue="1.23450005054474"
                   IntValue="12345"
                   StringValue="Hello, world!"
                   UriValue="http://www.davidpoll.com/"
                   util:AttachedProps.AttachedObject="Cool, huh?"
                   xmlns:util="clr-namespace:UtilitiesContent;assembly=UtilitiesContent">
    <util:NestedObject.DictValue>
        <System_mscorlib:String x:Key="Hello"
                                xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">Bonjour</System_mscorlib:String>
        <System_mscorlib:String x:Key="Goodbye"
                                xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">Au Revoir</System_mscorlib:String>
    </util:NestedObject.DictValue>
    <util:NestedObject.ListValue>
        <System_mscorlib:Int32 xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">1</System_mscorlib:Int32>
        <System_mscorlib:Int32 xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">2</System_mscorlib:Int32>
        <System_mscorlib:Int32 xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">3</System_mscorlib:Int32>
        <System_mscorlib:Int32 xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">4</System_mscorlib:Int32>
    </util:NestedObject.ListValue>
    <util:NestedObject.OtherObject>
        <util:Person Age="22"
                     FirstName="David"
                     LastName="Poll" />
    </util:NestedObject.OtherObject>
</util:NestedObject>

Nifty, right?  I thought so too.

And here’s the code it took to produce that XAML from my objects:

XamlSerializer xs = new XamlSerializer();
xs.DiscoverAttachedProperties(typeof(AttachedProps));
string text = xs.Serialize(this.nestedObject);

It’s pretty simple: create a XamlSerializer, discover relevant attached properties (you can do this per-property by handing it MethodInfo for the getter/setter, per-type, or per-assembly.  Note: the more of these you have, the more expensive it will be to serialize each object, since the serializer has to test each object for a value from each attached property that could possibly apply to it), and then serialize!

Turning this back into an object is as simple as:

XamlReader.Load(text);

The grand total for the size of this assembly: 36 kb uncompressed.  Bigger than I had originally hoped, but still small enough (16 KB compressed) to work for the scenario I had in mind, I think.  Regardless, it’s a far cry from the two serialization assemblies – both >100 KB uncompressed (which, incidentally, is not to knock them – they are probably much more rigorous than I am in ensuring correctness, performance, and supporting more than what XAML directly supports).

Did you hit any Silverlight roadblocks?

Nope.  For POCO serialization, I didn’t really hit any significant limitations or bugs in Silverlight.  The biggest  potential limiting factor is the lack of internal reflection, but since XAML only supports the public interfaces on objects, this didn’t up being an issue for what I was hoping to do.

UI must make a pact… we must bring salvation back…

Well, I couldn’t stop with just being able to serialize POCOs.  My first instinct after getting the XamlSerializer (basically) working was to try it on a Button and see what happens.  This is useful for serializing out user content (e.g. drawn paths, rich text, etc.), so it’s a nice functionality to have around.  I crossed my fingers, fixed some bugs, and gave it a shot… and failed.  Turns out it’s not so simple.  First of all, the UI stack in Silverlight has a number of controls with complex properties that on any other control would use a custom TypeConverter (and their WPF equivalents do indeed have these TypeConverters).  In Silverlight, however, many of these controls are native to the runtime, and are thus missing public managed TypeConverters that the XamlSerializer could look for when serializing.  Second, there are literally a million (ok, ok, not literally, that would be crazy – but it’s still a lot, alright?!) properties on every Silverlight control, and they probably shouldn’t all be serialized out.  I ought to be looking for DependencyProperties and checking to see if they’re set.  Finally, there are some types that just have to be treated specially: bindings, styles, and templates come to mind immediately.  With this in mind, I set about deriving from my XamlSerializer and creating a “UiXamlSerializer” that augments its functionality and handles these UI nuances.  Here’s what it does:

  • Understand the concept of DependencyProperties (attached and regular), and check to see if their values are set before serializing
  • Discover the built-in attached properties (e.g. Grid.Row and Grid.Column, which should “just work” with this serializer).
  • Handle TypeConversion for properties/types that have native TypeConverters
    • A few in particular are a bit sticky: TargetType on ControlTemplate and Style both need awareness of xml namespaces, as do Style Setters (for setting attached properties and finding the names of the DependencyProperties they target)
  • Reconstitute templates: ControlTemplate, DataTemplate, and ItemsPanelTemplate
  • Opportunistically detect references to items in Resources, and reference them using the “{StaticResource}” markup extension
  • Reconstitute bindings (use ReadLocalValue() on a DependencyProperty to get the BindingExpression, then serialize the binding instead of the value) – this is a behavior you can disable if you don’t want it
  • Recognize content inside of Blocks (i.e. rich text), and avoid rich XML formatting (this is mixed content – elements and direct content – and mustn’t have extra line-breaks or spaces in the XML when serialized)
  • Avoid a few built-in pitfalls (for example, some controls have properties where one or the other should be serialized, e.g. ListBox.SelectedItem, ListBox.SelectedItems, and ListBox.SelectedIndex)

Again, this is a fair amount of work, and it definitely goes a little farther than XamlWriter in WPF did (e.g. trying to serialize Bindings and StaticResources), but I wanted to see how close to full-fidelity I could get here.  Really, the only big item I couldn’t find a way to serialize were TemplateBindings.  Unfortunately, while you can get a TemplateBindingExpression from a property that’s been TemplateBound, I could find no way to get back to the name of the property to which it’s actually bound.  However, “{Binding RelativeSource={RelativeSource TemplatedParent}}” serializes just fine :).

The result of all of this is something that comes pretty close to doing what you want, serializing custom SDK, Toolkit, and custom controls just as happily as it serializes built-in controls.  Take a look at a simple demo:

UI Xaml Serialization Demo

Try selecting different items in the TreeView or ListBox, checking/unchecking the box, etc., and note how the serialized XAML keeps up with the changes you’ve made to the UI through your interactions.

The XAML used to produce the UI on the left-hand side of the screen is as follows:

<Grid x:Name="gridToSerialize"
        Grid.Column="0">
    <sdk:TreeView x:Name="treeView1"
                    Margin="4">
        <sdk:TreeViewItem Header="Level 0 - Item 0 (TreeView items)"
                            IsExpanded="True">
            <sdk:TreeViewItem Header="Level 1 - Item 0"
                                IsExpanded="True">
                <sdk:TreeViewItem Header="Level 2 - Item 0"></sdk:TreeViewItem>
                <sdk:TreeViewItem Header="Level 2 - Item 1"></sdk:TreeViewItem>
                <sdk:TreeViewItem Header="Level 2 - Item 2"></sdk:TreeViewItem>
            </sdk:TreeViewItem>
            <sdk:TreeViewItem Header="Level 1 - Item 1"></sdk:TreeViewItem>
            <sdk:TreeViewItem Header="Level 1 - Item 2"></sdk:TreeViewItem>
        </sdk:TreeViewItem>
        <sdk:TreeViewItem Header="Level 0 - Item 1 (Basic Controls)"
                            IsExpanded="True">
            <Button MinWidth="75"
                    MinHeight="23">Click!</Button>
            <CheckBox IsChecked="True">Check this out!</CheckBox>
            <ComboBox SelectedIndex="0">
                <ComboBoxItem>Combo box item 0</ComboBoxItem>
                <ComboBoxItem>Combo box item 1</ComboBoxItem>
                <ComboBoxItem>Combo box item 2</ComboBoxItem>
                <ComboBoxItem>Combo box item 3</ComboBoxItem>
            </ComboBox>
            <ProgressBar IsIndeterminate="True"
                            MinWidth="100"
                            MinHeight="23" />
            <sdk:DatePicker Height="23"
                            HorizontalAlignment="Left"
                            x:Name="datePicker1"
                            SelectedDate="7/23/2010"
                            Width="120" />
        </sdk:TreeViewItem>
        <sdk:TreeViewItem Header="Level 0 - Item 2 (Layout, Binding, Templates, &amp; Attached Properties)"
                            IsExpanded="True">
            <Grid xmlns:sys="clr-namespace:System;assembly=mscorlib">
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <ListBox Grid.Row="0">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding StringFormat='Value = {0}'}" />
                                <Slider MinWidth="100"
                                        IsEnabled="False"
                                        Value="{Binding}"
                                        Maximum="50"
                                        Minimum="0" />
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <VirtualizingStackPanel />
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                    <sys:Int32>10</sys:Int32>
                    <sys:Int32>20</sys:Int32>
                    <sys:Int32>30</sys:Int32>
                    <sys:Int32>40</sys:Int32>
                    <sys:Int32>50</sys:Int32>
                </ListBox>
                <toolkit:BusyIndicator Grid.Row="1"
                                        IsBusy="True" />
            </Grid>
        </sdk:TreeViewItem>
        <sdk:TreeViewItem Header="Level 0 - Item 3"></sdk:TreeViewItem>
    </sdk:TreeView>
</Grid>

Serializing this out using the UiXamlSerializer produces the following XAML:

<Grid xmlns="http://schemas.microsoft.com/client/2007"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Name="gridToSerialize"
        Grid.Column="0">
    <sdk:TreeView x:Name="treeView1"
                    Margin="4,4,4,4"
                    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
        <sdk:TreeViewItem Header="Level 0 - Item 0 (TreeView items)"
                            IsExpanded="True">
            <sdk:TreeViewItem Header="Level 1 - Item 0"
                                IsExpanded="True">
                <sdk:TreeViewItem Header="Level 2 - Item 0" />
                <sdk:TreeViewItem Header="Level 2 - Item 1" />
                <sdk:TreeViewItem Header="Level 2 - Item 2" />
            </sdk:TreeViewItem>
            <sdk:TreeViewItem Header="Level 1 - Item 1" />
            <sdk:TreeViewItem Header="Level 1 - Item 2" />
        </sdk:TreeViewItem>
        <sdk:TreeViewItem Header="Level 0 - Item 1 (Basic Controls)"
                            IsExpanded="True">
            <Button Content="Click!"
                    MinHeight="23"
                    MinWidth="75" />
            <CheckBox Content="Check this out!">
                <CheckBox.IsChecked>
                    <System_mscorlib:Boolean xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">True</System_mscorlib:Boolean>
                </CheckBox.IsChecked>
            </CheckBox>
            <ComboBox IsDropDownOpen="False"
                        SelectedIndex="0">
                <ComboBoxItem Content="Combo box item 0"
                                IsSelected="True" />
                <ComboBoxItem Content="Combo box item 1" />
                <ComboBoxItem Content="Combo box item 2" />
                <ComboBoxItem Content="Combo box item 3" />
            </ComboBox>
            <ProgressBar IsIndeterminate="True"
                            MinHeight="23"
                            MinWidth="100" />
            <sdk:DatePicker x:Name="datePicker1"
                            DisplayDate="7/25/2010"
                            FirstDayOfWeek="Sunday"
                            Height="23"
                            HorizontalAlignment="Left"
                            SelectedDate="7/23/2010"
                            Text="7/23/2010"
                            Width="120" />
        </sdk:TreeViewItem>
        <sdk:TreeViewItem Header="Level 0 - Item 2 (Layout, Binding, Templates, &amp; Attached Properties)"
                            IsExpanded="True">
            <Grid>
                <ListBox Grid.Row="0">
                    <System_mscorlib:Int32 xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">10</System_mscorlib:Int32>
                    <System_mscorlib:Int32 xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">20</System_mscorlib:Int32>
                    <System_mscorlib:Int32 xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">30</System_mscorlib:Int32>
                    <System_mscorlib:Int32 xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">40</System_mscorlib:Int32>
                    <System_mscorlib:Int32 xmlns:System_mscorlib="clr-namespace:System;assembly=mscorlib">50</System_mscorlib:Int32>
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <VirtualizingStackPanel CanHorizontallyScroll="False"
                                                    CanVerticallyScroll="False"
                                                    ScrollOwner="{x:Null}" />
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock FontSource="{x:Null}">
                                    <TextBlock.Text>
                                        <Binding Path=""
                                                 StringFormat="Value = {0}" />
                                    </TextBlock.Text>
                                </TextBlock>
                                <Slider IsEnabled="False"
                                        Maximum="50"
                                        Minimum="0"
                                        MinWidth="100">
                                    <Slider.Value>
                                        <Binding Path="" />
                                    </Slider.Value>
                                </Slider>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                <toolkit:BusyIndicator IsBusy="True"
                                        Grid.Row="1"
                                        xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" />
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
            </Grid>
        </sdk:TreeViewItem>
        <sdk:TreeViewItem Header="Level 0 - Item 3" />
    </sdk:TreeView>
</Grid>

There are a few things to notice here:

  • The DataTemplate for ListBox.ItemTemplate was serialized out and includes bindings for each of the items
  • Bindings are not serialized using the curly brace syntax (e.g. “{Binding Foo}”).  This is because they may (rarely) have properties attached to them, which requires that they be in object-element form
  • CheckBox.IsChecked got serialized the “long way” (object-element syntax).  Why?  Well, CheckBox.IsChecked isn’t of type bool, it’s a Nullable<bool>.  Because of this, it’s not a built-in type, and I serialize it out just like any other object (since I can’t determine the TypeConverter for it on a property level).  There is a built-in NullableBoolConverter TypeConverter, but it doesn’t support ConvertTo (to turn the bool back into a string) and wasn’t necessary to make this scenario work, so I decided to avoid bloating my code with a special case for this.
  • Path geometries do serialize using the UiXamlSerializer.  In Silverlight 4, we made the PathGeometry’s ToString() return the path mini-language, so making this work was actually fairly straightforward.

Once again, doing this serialization is quite straightforward.  Here’s the code:

UiXamlSerializer uxs = new UiXamlSerializer();
string text = uxs.Serialize(this.gridToSerialize);

Cool, right?

In order to keep the POCO XAML serialization assembly small, I chose to put this serializer in its own assembly, deriving from XamlSerializer and overriding a large set of virtuals in order to add understanding of these concepts to the serializer.  Getting UI XAML serialization adds a 26 kb assembly (uncompressed) on top of the 36 kb assembly for the base type.

Lucy in the sky…

Ok, so having a UiXamlSerializer is pretty cool, but what can it do?  Well, the first scenario that comes to mind for me is the serialization of rich text.  Silverlight 4 added the RichTextBox as a core control.  This control is great – it allows editing of rich text with a large variety of inline content, formatting, etc.  It also allows you to insert InlineUIContainers that support arbitrary content.  This is the only way to get images into your rich text.  RichTextBox does have a “Xaml” property, but this property is strictly limited to serializing out Paragraphs, Runs, LineBreaks, Spans, Bolds, Hyperlinks, Underlines, and Italics.  As a result, using this to serialize your rich text loses any images your users might have added to their rich text.  In order to save this data, you had to serialize this manually from the RichTextBox.Blocks property.

With the UiXamlSerializer, doing this is much simpler.  I’ve got a quick demo of this that gets a little “trippy”.  It’s a RichTextBox in which you can format text, add images, and even add other RichTextBoxes (visual recursion?  scary thought :)).  Clicking the button on the page will serialize the content out into XAML using the UiXamlSerializer, read it using XamlReader.Load(), then copy those freshly-cloned elements into the RichTextEditor on the right side of the screen.  Give it a shot!

Rich Text Serialization Demo

The code to do this is slightly more complex, simply because the RichTextBox.Blocks property can’t be serialized directly (its type has no public constructor).  Instead, I copy the elements to my own collection type that is XAML-friendly (remember, generic types aren’t supported in XAML in Silverlight), then copy them back after deserializing.  Otherwise, it’s pretty straightforward:

UiXamlSerializer uxs = new UiXamlSerializer();
ObservableObjectCollection ooc = new ObservableObjectCollection();
foreach (Block b in rteLeft.RichTextBox.Blocks)
    ooc.Add(b);
string xaml = uxs.Serialize(ooc);
this.xamlTb.Text = xaml;
ObservableObjectCollection bc = (ObservableObjectCollection)XamlReader.Load(xaml);
rteRight.RichTextBox.Blocks.Clear();
foreach (Block b in bc)
    rteRight.RichTextBox.Blocks.Add(b);

Et voila!  Simple RichTextBox content serialization!

Any Silverlight roadblocks this time?

Actually, there were only a few (fewer than I expected) painful things to overcome in Silverlight to make UI XAML serialization work:

  • Templates… Unfortunately, the only way to get the Template content in Silverlight is to expand the templates out.  As a result, I have to handle each type of template specially.  DataTemplates are easy: LoadContent() and then serialize the content into the DataTemplate element.  ControlTemplates are reasonably simple as well: create an instance of the control, apply the template, and serialize the visual child of the control.  ItemsPanelTemplates are a little more difficult: create an ItemsControl, apply the template, put the ItemsControl in a popup to force its template to expand, search its visual children for the ItemsPresenter, and serialize the ItemsPresenter’s visual child.  Confusing, but doable.  Unfortunately, all of these require actually expanding the template, creating all of objects in its visual tree.  It’s not ideal, but it works.
  • TypeConverters… I hope I got all of these, but I’m sure there are some that I’ve missed.  I think the primary scenarios all work, though.  Let me know if you encounter an issue and I’ll see if there’s anything I can do to fix it.
  • ShouldSerialize… In WPF, there are a number of ShouldSerializeProperty methods hidden from intellisense and the object browser.  These tell the XamlWriter whether a particular property should be serialized (e.g. should I serialize SelectedItem, SelectedItems, or SelectedIndex on a ListBox?)  I’ve tried to hit the worst offenders here, but again, I’m sure I’ve missed some.

The following issues were insurmountable, but I made a best effort:

  • TemplateBindings… they just can’t be reconstituted once they’ve been created.  There’s no way to get the property that is the Source of the TemplateBinding back after it’s been created.
  • DependencyProperties… Since you can’t get their names, I have to rely on the reflected field name of the DependencyProperty.  I make the (usually safe) assumption that this field will be public, static, and named <PropertyName>Property in both the regular and attached property cases.  If I don’t find these fields, I treat the property as a CLR property rather than a DependencyProperty.
  • Bindings…  Everything’s good when trying to rebuild these except that the PropertyPath doesn’t expose enough information to safely rebuild the Binding’s path.  In particular, if the Binding has an attached property within its path, it uses the “(ns:Foo.Bar)” syntax.  Since I don’t know what “ns:” means after the binding was originally parsed, I can’t be 100% sure what the attached property really is.  In this case, the UiXamlSerializer searches its known attached properties for a class named “Foo” with an attached property called “Bar”.  If it finds multiple attached properties that match this, the result is indeterminate.  In other words, please don’t create a class called “Grid” with an attached property called “Row” and bind to that property – you’re asking for trouble.  If you call it “MyGrid”, though, you’re golden :)
  • Weirdness… I can probably work around these, but now and then you’ll encounter some little quirks or bugs in Silverlight’s XAML parsing.  It’s much less unpredictable in Silverlight 4 than Silverlight 3, but there are still one or two issues.  For example, if you explicitly set a control’s Cursor property to null in code, all is well.  Unfortunately, doing the same explicitly in XAML throws.  Luckily, it’s rare to set this property’s value to null in code, since its default value is null anyway (and if the DependencyProperty remains unset, the serializer won’t write it out, and everybody’s happy).  I haven’t special-cased this for now, but you get the picture :).

Zippity Doo Dah…

One place where I learned a lot from this experience was in dealing with performance in Silverlight.  It can be a sticky area, and I definitely had my fair share of performance problems when I made my first pass on implementing this.

Honestly, I had never actually profiled a Silverlight application before trying this.  It had rarely been an issue for me with the things I was writing.  But then I wrote the UiXamlSerializer, and when I tried to Serialize about 60 lines of XAML it took about 1.2 seconds.  Simple profiling revealed a poor coding choice I had made that increased the complexity of my serialization by a factor of O(n^2) – oops!  Now, serializing the same content (after warming up the serializer, which now does some aggressive caching) takes about 120 milliseconds – just a little longer than WPF’s XamlWriter takes for the same content: 80 milliseconds (also after warmup).  That said, my “warmup” takes about 1/3 of the time of XamlWriter’s (I suspect I’m doing less – I think XamlWriter actually searches all loaded assemblies for attached properties – I do it explicitly for desired attached properties).  Not too shabby, right?  With more effort, I could probably improve on that some more, but I was pretty happy with the improvement :).

The best resource I found for learning to profile a Silverlight application was Maxim Goldin’s blog.  He lays it out quite clearly.  Thanks, Maxim!

Here are some of the big things I learned from profiling and implementing performance improvements:

  • Reflection is expensive.  I knew this going in, but I didn’t realize how much of an impact it would have.  I cut my run-times nearly in half just by caching any attributes, PropertyInfos, and MethodInfos I happened to find along the way.  It’s a fairly easy step to take for a big win.  GetCustomAttributes() was the biggest offender for me.  These can’t change at runtime anyway, so they’re safe to cache.  I just kept dictionaries associating them with Types, PropertyInfos, and MethodInfos whenever I could.
  • LCG (Lightweight Code Generation) is awesome.  I managed to shave off a good 30-40% of my time by turning PropertyInfo.GetValue() and MethodInfo.Invoke() calls into pure IL which I could invoke directly.  Granted, this project is very heavy on reflection, so I probably got more of a benefit here than most apps would, but I was really surprised by just how much of a difference it makes.  It’s important to cache the generated IL here, since the initial generation is actually more costly than reflection, but if you’re repeatedly checking the same property’s value over and over on different objects (e.g. “Background” on any control), this makes a big difference in aggregate.  Furthermore, subsequent serializations (e.g. calling Serialize() 50 times on 50 different objects of the same type) are significantly faster.
  • LINQ expressions make LCG easy!  Wow — I was stunned by just how simple it was to do my lightweight code generation.  Here’s an example of the code it took to generate a delegate (Func<object, object> in this case) for an attached property getter (this.Getter is the MethodInfo for the attached property, and this.GetterFunc is the cached generated delegate):
var param = Expression.Parameter(typeof(object));
var cast = Expression.Convert(param, this.TargetType);
var call = Expression.Call(null, this.Getter, cast);
var finalCast = Expression.Convert(call, typeof(object));
var lambda = Expression.Lambda<Func<object, object>>(finalCast, param);
this.GetterFunc = lambda.Compile();
  • Cache often.  It probably goes without saying, but assuming you’ve already got algorithmic complexity down, look for places to cache things rather than performing new calculations.  Something to watch out for: expensive lookups.  I found that my aggressive caching in Dictionaries led to a new potential performance issue – the Dictionary lookups.  A few times, I needed to go through and optimize the Equals() and GetHashCode() implementations for my dictionary keys.  For Types, this was no problem for me, but PropertyInfos are more complex (they don’t have an Equals() implementation, so I had to write one myself).
  • Caching has an impact on assembly size.  The IL metadata from additional fields and code for caching added at least 4-5 KB to my assemblies.  It had a significant impact on performance, though.  It’s a trade-off one must consider when making these types of improvements.  In my case, the gains were so great that it was really a no-brainer.  As I was looking at smaller optimizations, however, I began to consider this issue carefully.
  • Watch out for Exceptions.  First chance exceptions make this serializer run fairly slowly when the debugger is attached, and they’re happening constantly because some attached properties target any DependencyObject, but throw for all but a few types.  As a result, almost every UIElement being serialized hits a first chance exception.  I spent some time clearing them out of my own code, and it definitely improved performance both with and without the debugger attached.  It’s an unavoidable issue sometimes (in this case because I’m running someone else’s code blindly for property getters), but minimize using exceptions for control flow whenever possible.
  • Finally, LINQ to Objects is really cool and makes for some very clean code.  Watch out for its performance pitfalls, though.  One example: once you create a query, iterating over the resulting IEnumerable re-runs the query each time.  If you’re doing any more complex calls within those queries (e.g. selecting the value of a property via reflection, performing string comparisons, or determining if the property can be written to XAML), watch out for this.  “let” and ToArray() are your friends in this case.

I heard it through the grapevine…

Before I conclude this post, I just want to take a moment to acknowledge some folks who tried this long before I ever got my hands on it.  Having a XamlWriter equivalent in Silverlight is a request I’ve seen over and over, and others have tried it before me.  My hope was to overcome some of their drawbacks and really have something general-purpose.  I think, for the most part, I’ve accomplished that.  Regardless, check out these – they may do the trick even better than I do!

All of these folks have done great work and deserve recognition for it.  I may have missed some – these were just from a preliminary search.

I got plenty o’ plenty…

You know I would never leave you without code and links to the live sample!  Last night, I updated SLaB to v0.9 (uh oh, I’m running out of numbers, but I’m not sure I want to have a “v1.0”… any suggestions?) and updated the live sample to include XamlSerializer demos.

I’ve also updated my SLaB sample to be installable!  Yup, it uses Transparent Platform Extensions and can run out of browser.  How?  That’s my RemoteControl (which uses my XapLoader) at work.  I’ll eventually blog about it specifically, but there’s more work I’d like to do there :).

With that said, here’s a summary of the links and access to the source!

  • Live Sample (source — found in the SLaB v0.9 source under “UtilitiesContent”)
  • SLaB v0.9 (includes source, a sample app, some tests, and binaries)
    • For the latest version, please check out SLaB on my Downloads and Samples page.
    • The v0.9 download of SLaB includes the following changes:
      • Added XamlSerializer and UiXamlSerializer for producing Xaml from POCOs or UI/DependencyObjects
      • Updated MEFContentLoader to include Glenn Block’s changes from his talk, supporting MEF’s Xap loading (sorry for the long delay on this – didn’t realize those changes had been made until someone brought it up to me!)
      • Fixed a bug with PackUriParser that caused navigations between pages within the same assembly (using full pack Uris) not to update the browser’s address bar as users moved from page to page.
      • Other minor bugfixes

As always, I love hearing what you have to say.  This is something I primarily do in my “copious” amounts of free time (yes, the quotes indicate my sarcasm) because I really enjoy working on/with Silverlight and seeing how far I can push it/myself.  I also like keeping my developer side alive and well by spending some quality time coding (have to break that Program Manager stereotype :)).  I’m always surprised by how 6 PM suddenly turns into 4 AM when I’m working on something like this.  Anyhow, if there’s something you’d like to see or have a question about, speak up!  I’ll try to get back to you when I can, but I reserve the right to work on things I’m intrigued by! ;)

43 thoughts on “To XAML, with love (an experiment with XAML Serialization in Silverlight)”

  1. Ok I’m impressed :)

    Of course everything you have done (and posted about), is very important and helpful. However, if you want to concentrate on the thing that will be really useful to “the masses”, it is an editor that saves the data to the database and then displays it.

    It appears that limiting the ability of what the editor can do (for now) would make it usable right now?

    If so, if you made an example that:

    1) Loads up what you last saved from a database and displays it.
    2) Allows you to create new content and save to the database.

    It will be a widely used piece of code. :)

    1. Michael,

      Thanks for the kind words :)

      I’m not 100% sure I understand your suggestion. When you say an “editor”, are you talking about a rich text editor? Also, when you’re referring to a “database”, are you referring to one on the client or on the server?

      I’d love to know more about the scenario you have in mind :) What would you like to use it for?

      1. Editor – The Rich Text Editor
        Database – A Database on the server

        For you this is all very trivial, but for many beginners out there (perhaps the majority of Silverlight developers have less than one year) this is not obvious.

        I believe that developers would like to have a Silverlight application, where a user can use the Rich Text Editor to create a document and then save it to the database on the server.

        1. I am also looking for this kind of SL application.

          1. graphical drawing (pbrush like UI)
          2. save xaml to database
          3. load xaml to SL application from database

  2. Wow, *awesome* post! I’m interested in seeing how I can integrate this into a library I’m writing (http://github.com/xpaulbettsx/reactivexaml) – serialization is a big part of v.Next and my original serialization plan was to use JSON, but you’ve made some very compelling arguments to use Xaml as the serialization implementation, as well as really understanding the details of serializing objects in Silverlight in detail.

  3. Pingback: DotNetShoutout
  4. Simply awesome! I can’t wait to try out the UiXamlSerializer and all the other SLaB goodies.

    thanks for the performance tips and keeping the binairies as small as possible.

    Greg

  5. Hi David,

    Thanks for a great post. I was particularly interested in the section and demo related to UI XAML Serialization and have a couple of questions:

    1) One application I see for this is the serialization of a user-defined Silverlight GUI. For example, a WYSIWYG GUI Editor that allows the user to visually define a GUI on a design surface and have the application serialize this. Do you have any comments or suggestions about such an application of XAML Serialization?

    2) I am particularly interested in your thoughts on an approach for the reverse of 1) above. A scenario would be a Silverlight application that reads a XAML UI definition and dynamically instantiates the components that make up the UI. Can you comment on this or point me to any documentation that may help in determining an approach for such a scenario?

    Any suggestions would be much appreciated,

    Mustafa.

    1. Mustafa,

      I’m glad you liked the post! To your questions:
      1) This is definitely possible. In fact, it’s basically the rich text scenario above (where a user, through gestures — in this case typing and clicking buttons) can modify the UI, and then it can be serialized out to XAML. My “design surface” was limited in its capabilities to rich text, images, and other rich text editors, but you can easily imagine doing richer layout and serializing other controls there.

      2) This is doable, but you’ll want to be careful with loading arbitrary XAML that has been created by users. Specifically, a user could create an instance of something that does work (e.g. makes a network call), and that’s happening within your application, so it has all of the permissions available to clients on your domain. Likewise, a user could create a component that references an application-wide resource that you intended to keep private. You could unintentionally open up a security hole. Just like with XSS-prevention, you’ll (at a minimum) want to do something to cleanse/strip the XAML of potentially dangerous content. Otherwise, it’s as simple as calling XamlReader.Load() on the XAML :)

      -David

      1. David,

        Thanks for the fast response. Your comments are very helpful. Regarding point 2, for the scenario I have in mind, the XAML is to be generated by a visual GUI designer application, so the user will not have the opportunity to manipulate the XAML directly, avoiding the type of security risks you’ve pointed out.

        I have a couple of more questions regarding the use of XamlReader.Load() to dynamically render a GUI:

        1) Are there any performance considerations to take into account? For example, if the GUI defined by the XAML is complex, can the entire XAML content be loaded at once with XamlReader.Load() or is it better to break the XAML into fragments that are loaded separately?

        2) Does XamlReader.Load() support references to event handlers that are contained in separate assemblies? If so, are there any considerations when doing this? In my scenario, the event handlers trigger the actions that control the interactivity of the GUI and related business rules.

        Once again, any insights would be appreciated,

        Mustafa.

        1. Mustafa,

          Here are some more responses to your questions :)
          1) For XamlSerialization, complexity should (in theory — if I haven’t done something really broken :)) increase linearly with the size of the XAML. For XamlReader.Load(), this is the same parser that is used when you’re doing Application.LoadComponent() for all of your UserControls, etc. The same considerations for performance characteristics apply.
          2) No — XamlReader.Load() doesn’t support event handlers. For this, you must use Application.LoadComponent(), which requires the XAML to actually be embedded in the assembly (for security reasons). If you’re working with Actions, etc., these usually operate through reflection, which should allow them to work in spite of any assembly boundaries. Please be careful, though, with loading code in arbitrary/user assemblies. That code will run from your domain, so cross-domain access can be compromised.

    1. Sorry for the delayed response — I’ve been a bit busy with work :). There are 2 dlls that can be used independently: one for the POCO serializer and one for the UI serializer. The UI Serializer depends on the POCO one, but otherwise, they require no other dlls. You can find them in my SLaB download.

  6. Hi, David.

    Thank you for extraordinary job you’re making here with SLaB and posts about HOW you do it. :)
    For me it is VERY educative and interesting.

    While playing with your XAML Serializer I’ve discovered 2 issues.

    1. in your POCO Serialization demo, when pressing “Convert to XAML” button I get an error:
    ______________________________________________
    Failed to create a ‘System.Double’ from the text ’1,23450005054474′. [Line: 1 Position: 104]

    at MS.Internal.XcpImports.CreateFromXaml(String xamlString, Boolean createNamescope, Boolean requireDefaultNamespace, Boolean allowEventHandlers, Boolean expandTemplatesDuringParse)
    at MS.Internal.XcpImports.CreateFromXaml(String xamlString, Boolean createNamescope, Boolean requireDefaultNamespace, Boolean allowEventHandlers)
    at UtilitiesContent.XamlSerializerDemo.Button_Click(Object sender, RoutedEventArgs e)
    at System.Windows.Controls.Primitives.ButtonBase.OnClick()
    at System.Windows.Controls.Button.OnClick()
    at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
    at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
    at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)
    ______________________________________________

    2. When playing with RichTextBox Serialization a thought stumbled in my mind “what if serialize not just to a string, but to parse an xml document from it. Thus it can be readable in xaml form and relatively easy edited.” Here is the situation:

    I have a RichTextBox (RTB) and a TextBox for XAML viewing. When I press a button, RTB is serialized block by block, as in your post, then an XDocument is parsed from the string and displayed in TextBox like

    TextBox.Text = XDocument.ToString();

    . In teh TextBox I see nice and formated “XML”. On pressing another button, TextBox.Text property is deserialized into blocks again and placed in the starting RTB.

    The funny moment is seen when doing convention a couple of times. Strangely some empty spaces begin to occur in document and time for serialization/deserialization procedure increases exponentially.

    1. Look s like an internationalization problem: 1.234 != 1,234
      this seems to be fixed in the downloadable source (0.11)
      @depoll: why EnUsCulture, not CultureInfo.InvariantCulture ?

  7. P.S. I’ve found that this strange behavior occurs because text in TextBox.Text property is formated and looks pretty. But correct me if I’m wrong, this shouldn’t happen?

  8. HI ,
    nice post , this is what i am looking for
    but in this article we can serialise to xaml and back to text editor
    is there a way where we can convert it to html
    andback to editor
    i need html version, so that it can be saved t database & also html can be used as template to send out an email in that format

  9. I’m curious to know what you’d recommend for handling structure maximum and minimum values like double.MaxValue and double.MinValue. I have a class that has a property whose default value is set to double.MaxValue. Your serializer handles it without difficulty, but XamlReader.Load() throws a XamlParseException exception similar to the following:

    ‘Failed to create a ‘System.Double’ from the text ’1.79769313486232E+308′

    1. The built in TypeConverter for doubles treats them as floats, which is why double.MaxValue won’t work here. You should be able to, however, use a custom TypeConverter that handles large doubles.

  10. Hi David,

    Thanks for your work, it’s awesome material.

    Although I’ve found a little issue:

    If I display a TextBlock with the following markup:

    I got this serialized result:

    So the final textblock will contain it’s text twice.
    Do you have any idea how to avoid this?

    Thanks again!

    1. If I display a TextBlock with the following markup:
      I got this serialized result:
      So the final textblock will contain it’s text twice.

      Ah, the markup disappeared, I try with HTML encoded text.

      So the initial markup:
      <TextBlock FontSource="{x:Null}" Text="footext">
      <Run Text="footext" />
      </TextBlock>

      And the result:

      <TextBlock FontSource="{x:Null}" Text="footext">
      <Run Text="footext" />
      </TextBlock>

      Thanks,

  11. And the final, corrected, encoded and fixed code: :P

    <TextBlock Text="footext">
    </TextBlock>

    Result:

    <TextBlock FontSource="{x:Null}" Text="footext">
    <Run Text="footext" />
    </TextBlock>

  12. David, I’ve scoured the web all day to the solution to serializing the contents of the RichTextBox. Thanks for all the hard work!

    I wonder though if you could help with a problem I keep getting? I’ve tried this in a small VB mock up and in a rather large C# project I’m working on, but I get the same error in both projects.

    UiXamlSerializer uxs = new UiXamlSerializer();
    ObservableCollection ooc = new ObservableCollection();
    foreach (Block b in rtb.Blocks)
    ooc.Add(b);
    string xaml = uxs.Serialize(ooc);

    Error:-

    Token EndDocument in state Document would result in an invalid XML document.

    I’m getting this even if the RTB is empty, i.e. pressing the save button just after the project is loaded.

    Thanks,
    Russ

  13. What a great bit of work. Thank you for this!

    May I ask a question: when, in Xaml, you use x:Name to create a class field and link to it a part of the UI, what form does that take when introspecting with the Reflection API? These appear to be internal fields, but I don’t see how to find them with the API … now I haven’t read all of your code, and it likely is there, but I wondered if you could assist …

    Also … an observation … there appears to be a lot of frustration in the Silverlight Xaml community over behavior that was in one release, but is not now, having to do with Namescopes. CreateFromXaml was “here” in one release, and it was somewhat replaced with Application.LoadComponent and XamlReader.Load … but not quite, as these appear to keep information in an internal (or really hard to get at) location that ultimately seems to require x:Name should not be used. Your XamlSerializer (and a corresponding Deserializer) could provide a way around the problem, it seems, by providing a way to both use a Xaml to instantiate a new UI, and then subsequently hack around the Namescope problem by replacing the x:Name uses with placeholders that would permit linking of loaded components with FindName (using the replacement name).

    Now my thought might be nothing short of silly, and you might have an alternative fix. pBut it seems that, especially if one could learn how to reflect on these internal fields in combination with your Serializer, a somewhat painful problem could be sidestepped until the Silverlight team put that functionality back.

    Again, thanks for your work … very helpful to those of us who are, in comparison, significantly new to this space.

  14. Well as luck would have it, I found the answer to the internal field question. So … I will get back to regarding its use + the use of your serializer …

  15. Hi David,
    Thanks for the great job you have done.
    I tried to use your UIXamlSerializer generating a xaml file in Silverlight and loading it from WPF, unfurtunatelly it did not work with many standard controls from Silverlight, the WPF parser complains with certain properties (like “Data” inside a polygon object for example).
    Do you have any advise for me on this matter?

    One of the specific error messages I am getting from the parser says:
    Cannot set unknown member ‘Microsoft.Expression.Shapes.RegularPolygon.Data’

  16. My Hero! Finally, someone who understands Xaml is not just for friggin UI controls… every implementation I found w/ XamlWriter were specifically for UI… sigh. Your solution saved the day! Great work.

  17. Hello David,

    Thanks for sharing your knowledge with us.
    I am using your XamlSerializer.
    I have run across the following problem.

    I Create a rectangle in blend with a gradient fill.
    I load it in silverlight using XamlReader.Load()
    and it looks good. I then export it using your
    UIXamlSerializer.Serialize().
    It does not output the colors or the gradient stops.

    Here is what i load into silverlight.

    And here is what get exported using the uiserializer.

    Any suggestions on what to try to get the colors and gradient stops exporting?

    Thank you for you time.

  18. Hello David,

    Thanks for sharing your knowledge with us.
    I am using your XamlSerializer.
    I have run across the following problem.

    I Create a rectangle in blend with a gradient fill.
    I load it in silverlight using XamlReader.Load()
    and it looks good. I then export it using your
    UIXamlSerializer.Serialize().
    It does not output the colors or the gradient stops.

    Here is what i load into silverlight.

    
        
          
            
            
            
          
        
      
    

    And here is what get exported using the uiserializer.

    
            
              
                
                  
                
                
                  
                
              
            
          
    

    Any suggestions on what to try to get the colors and gradient stops exporting?

    Thank you for you time.

  19. Hello David,
    I am very thankful for sharing your code, works awesome! I just using serialization. that only required 2dlls:
    SLaB.Utilities.Xaml.Serializer
    SLaB.Utilities.Xaml.Serializer.UI

    var uxs = new SLaB.Utilities.Xaml.Serializer.UI.UiXamlSerializer();

    string mystrXAML = uxs.Serialize(MyPanel);

    but in my panel, I had a control that has a Polyline and other stuff. After Serialization, the Points in the Polyline were delete. but the good thing, I can serialize the Polyline alone, like:
    string myPLXAML = uxs.Serialize(MyPanel.PLControl.polyline);
    polyline is the name of Polyline, I try to create a public property like

    public Polyline MyPolyline
    {
    get { return polyline; }
    set { polyline=value; }
    }
    But doesn’t work… any idea????

    1. I solved the problem, exposing the Points instead the Polyline. Look like the Polyline was override and lose the the binding.
      public PointCollection MyPoints
      {
      get { return polyline.Points; }
      set { polyline.Points=value; }
      }

  20. David,

    Let me begin by saying “thank you” for sharing your hard work with the rest of us journeyman developers.

    Your blog entry (and project) is entitled “Silverlight and Beyond” but my interest at the moment is WPF. I have been developing an application that permits my users to create a visual layout, initially populated with UI elements that derive from System.Windows.Shapes.Shape. Now I need to incorporate support for some elements which derive from System.Windows.Controls.UserControl. Of course, I need copy/paste capability and, for the Shape-derived elements, was successfully using the XamlWriter/XamlReader approach to make a deep copy of the UI element. Unfortunately, this doesn’t work for the UserControl-derived elements (specifically, XamlReader throws an exception when the converter defined in is processed, saying that the x:Key already exists in the ResourceDictionary.)

    Perhaps you could be so kind as to help me with answers to two questions:
    1) can you suggest any way I might work around the exception I am getting from XamlReader?

    2) would the UiXamlSerializer/UiXamlDeseserializer you have put together for SLaB be appropriate for my needs?

    Thanks in advance for your help.

  21. Hi i am using your code to read my xaml controls

    uxs.Serialize(rootObject)

    However it is very slow, could you please assist.
    note: the size of my xaml is 68Kb

  22. Hi ,

    I am using the following code.

    UiXamlSerializer oUiXamlSerializer = new UiXamlSerializer();
    oUiXamlSerializer.Serialize(Object);

    Its takes +-30 second to process the file.

    Could you please investigate. the file size is 48KB

Leave a Reply