Click here to Skip to main content
Click here to Skip to main content

An XAML Serializer Preserving Bindings

By , 1 Nov 2007
Rate this:
Please Sign up or sign in to vote.

Introduction

Unfortunately XamlWriter is not capable of preserving bindings (see Serialization Limitations of XamlWriter.Save for more details). I have Googled for a couple of days now but I did not find any solution yet (if somebody already has one, please let me know).
Therefore I created the XamlSerializer class which can save Bindings.

Background

While Googling, I figured out that it is possible to get some information about how to serialize an object by using MarkupWriter.GetMarkupObjectFor; it returns a MarkupObject for the provided object. For all properties of the object which should be serialized, the MarkupObject provides a MarkupProperty in its MarkupObject.Properties collection.
Using that MarkupObject of the root object, it is possible to dig through the object's properties and contained objects.

How Does It Work?

Equipped with that information, I have started implementing XamlSerializer which is capable of writing XAML and storing property bindings. Since XamlReader has no difficulties in understanding bindings, XamlSerializer is loading XAML by using XamlReader.Load.

In XAML, each object is represented by a tag with the name of the object's type; properties are either stored as attributes or also as tags (composite properties). For content properties (specified by the ContentProperty attribute on the containing class) only their values are stored. Therefore XamlSerializer creates XAML using XmlDocument, because then it is not necessary to sort properties before storing them (attributes first, then elements for composite properties and finally the value of the content property), instead they can be stored in the order MarkupObject.Properties which returns by just adding an attribute or appending a child element.

Saving an object is done with the SaveObject method which expects the parent XmlElement and an appropriate MarkupObject.

private void SaveObject(XmlElement parent, MarkupObject markupObject)

SaveObject calls SaveProperty for all properties returned by MarkupObject.Properties passing the XmlElement of the object, the MarkupObject and the corresponding MarkupProperty.

private void SaveProperty
    (XmlElement parent, MarkupObject markupObject, MarkupProperty property)

SaveProperty first checks if it is a dependency property (MarkupProperty.DependencyProperty). If it is one, BindingOperations.GetBinding is called to get the Binding for that property. If a Binding exists the property is stored as an element, the retrieved Binding is stored using the MarkupObject returned by MarkupWriter.GetMarkupObjectFor called with that Binding. Bound properties are stored in SaveBindingProperty.

private void SaveBindingProperty
    (XmlElement parent, MarkupProperty property, Binding binding)

If the property is not a dependency property, it is checked if it is the content property of the object then only its content is stored using SavePropertyContent.

private void SavePropertyContent(XmlElement parent, MarkupProperty property)

Composite properties are stored using an XmlElement within SaveCompositeProperty which calls SavePropertyContent to store the property's value.

private void SaveCompositeProperty(XmlElement parent, MarkupProperty property)

And finally properties which can be stored as strings in attributes are saved by SaveAttributeProperty.

private void SaveAttributeProperty(XmlElement parent, MarkupProperty property)

Namespaces

For all objects and properties which are not contained in the namespace if the root object XAML expects appropriate prefixes and URI definitions. These are generated by GetNamespacePrefix and GetNamespaceUri. Both methods are expecting a Type to retrieve the namespace and the assembly.

private string GetNamespacePrefix(Type type)
private string GetNamespaceUri(Type type)

For each namespace/assembly combination a prefix and a URI definition of the form xmlns:PREFIX="clr-namespace:NAMESPACE;assembly=ASSEMBLY" is added to the DocumentElement of the XmlDocument. For namespaces which are from the assembly PresentationFramework, the URI http://schemas.microsoft.com/winfx/2006/xaml/presentation is used. Prefixes are stored in a Dictionary to avoid multiple definitions.

Using the Code

I have created the project with Microsoft Visual C# 2008 Express Edition (.NET 3.5); I have no idea if it is running with .NET 3.0.

Using XamlSerializer is really easy: It provides some static methods to load and save XAML. To load an object stored in XAML, you can use Load while saving is done with Save (I guess this is obvious).

//==========================================================================
/// <span class="code-SummaryComment"><summary></span>
///   Saves the provided object as XAML into the specified 
/// <span class="code-SummaryComment"><see cref="XmlWriter"/>.</span>
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="instance"></span>
///   The <span class="code-SummaryComment"><see cref="Object"/> to save.</span>
/// <span class="code-SummaryComment"></param></span>
/// <span class="code-SummaryComment"><param name="writer"></span>
///   The <span class="code-SummaryComment"><see cref="XmlWriter"/> to use for writing the XAML.</span>
/// <span class="code-SummaryComment"></param></span>
public static void Save(object instance, XmlWriter writer);

//==========================================================================
/// <span class="code-SummaryComment"><summary></span>
///   Saves the provided object as XAML into the specified 
/// <span class="code-SummaryComment"><see cref="TextWriter"/>.</span>
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="instance"></span>
///   The <span class="code-SummaryComment"><see cref="Object"/> to save.</span>
/// <span class="code-SummaryComment"></param></span>
/// <span class="code-SummaryComment"><param name="writer"></span>
///   The <span class="code-SummaryComment"><see cref="TextWriter"/> to use for writing the XAML.</span>
/// <span class="code-SummaryComment"></param></span>
public static void Save(object instance, TextWriter writer);

//==========================================================================
/// <span class="code-SummaryComment"><summary></span>
///   Saves the provided object as XAML into the specified 
///   <span class="code-SummaryComment"><see cref="Stream"/>.</span>
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="instance"></span>
///   The <span class="code-SummaryComment"><see cref="Object"/> to save.</span>
/// <span class="code-SummaryComment"></param></span>
/// <span class="code-SummaryComment"><param name="stream"></span>
///   The <span class="code-SummaryComment"><see cref="Stream"/> to use for writing the XAML.</span>
/// <span class="code-SummaryComment"></param></span>
public static void Save(object instance, Stream stream);

//==========================================================================
/// <span class="code-SummaryComment"><summary></span>
///   Saves the provided object as XAML into the specified 
/// <span class="code-SummaryComment"><see cref="string"/>.</span>
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="instance"></span>
///   The <span class="code-SummaryComment"><see cref="Object"/> to save.</span>
/// <span class="code-SummaryComment"></param></span>
/// <span class="code-SummaryComment"><returns></span>
///   A <span class="code-SummaryComment"><see cref="string"/> containing the XAML.</span>
/// <span class="code-SummaryComment"></returns></span>
public static string Save(object instance);

//==========================================================================
/// <span class="code-SummaryComment"><summary></span>
///   Reads the XAML in the specified <span class="code-SummaryComment"><see cref="Stream"/> and returns </span>
///   the root object.
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="stream"></span>
///   The <span class="code-SummaryComment"><see cref="Stream"/> providing the XAML.</span>
/// <span class="code-SummaryComment"></param></span>
/// <span class="code-SummaryComment"><returns></span>
///   The root object of the read XAML.
/// <span class="code-SummaryComment"></returns></span>
public static object Load(Stream stream);

//==========================================================================
/// <span class="code-SummaryComment"><summary></span>
///   Reads the XAML in the specified <span class="code-SummaryComment"><see cref="XmlReader"/> and returns </span>
///   the root object.
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="reader"></span>
///   The <span class="code-SummaryComment"><see cref="XmlReader"/> providing the XAML.</span>
/// <span class="code-SummaryComment"></param></span>
/// <span class="code-SummaryComment"><returns></span>
///   The root object of the read XAML.
/// <span class="code-SummaryComment"></returns></span>
public static object Load(XmlReader reader);

//==========================================================================
/// <span class="code-SummaryComment"><summary></span>
///   Reads the XAML in the specified <span class="code-SummaryComment"><see cref="string"/> and returns </span>
///   the root object.
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="value"></span>
///   The <span class="code-SummaryComment"><see cref="string"/> providing the XAML.</span>
/// <span class="code-SummaryComment"></param></span>
/// <span class="code-SummaryComment"><returns></span>
///   The root object of the read XAML.
/// <span class="code-SummaryComment"></returns></span>
public static object Load(string value); 

Summary

Although there are still a lot of issues and bugs, XamlSerializer now makes it possible to preserve bindings when serializing objects to XAML. I have only done some basic tests with easy XAML files and I am sure there are many scenarios XamlSerializer cannot handle yet; but it will hopefully be a good starting point.

History

  • 2007-11-01: Article submitted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

boris_richter
Software Developer
Germany Germany
No Biography provided

Comments and Discussions

 
QuestionNew article PinmemberMember 444388525-Mar-10 7:16 
GeneralBinding Preserving PinmemberAlexDov30-Mar-08 23:11 
GeneralRe: Binding Preserving PinmemberNick Polyak13-May-08 4:37 
GeneralRe: Binding Preserving PinmemberAlexDov30-Jun-08 2:00 
GeneralRe: Binding Preserving PinmemberNick Polyak4-Jul-08 3:36 
GeneralRe: Binding Preserving PinmemberAlexDov4-Jul-08 3:56 
GeneralRe: Binding Preserving (a little correction) [modified] PinmemberNick Polyak13-May-08 4:47 
GeneralRe: Binding Preserving PinmemberLiveUltimate13-May-08 9:59 
QuestionRe: Binding Preserving [modified] Pinmembercivanovici30-May-08 4:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 1 Nov 2007
Article Copyright 2007 by boris_richter
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid