Click here to Skip to main content
Licence CPOL
First Posted 1 Nov 2007
Views 22,854
Downloads 217
Bookmarked 16 times

An XAML Serializer Preserving Bindings

By | 1 Nov 2007 | Article
Saving XAML files using MarkupWriter

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).

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

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

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

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

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

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

//==========================================================================
/// <summary>
///   Reads the XAML in the specified <see cref="string"/> and returns 
///   the root object.
/// </summary>
/// <param name="value">
///   The <see cref="string"/> providing the XAML.
/// </param>
/// <returns>
///   The root object of the read XAML.
/// </returns>
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

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionNew article PinmemberMember 44438857:16 25 Mar '10  
QuestionException occurred! PinmemberVankataBS21:24 1 Aug '08  
GeneralBinding Preserving PinmemberAlexDov23:11 30 Mar '08  
GeneralRe: Binding Preserving PinmemberNick Polyak4:37 13 May '08  
GeneralRe: Binding Preserving PinmemberAlexDov2:00 30 Jun '08  
GeneralRe: Binding Preserving PinmemberNick Polyak3:36 4 Jul '08  
GeneralRe: Binding Preserving PinmemberAlexDov3:56 4 Jul '08  
GeneralRe: Binding Preserving (a little correction) [modified] PinmemberNick Polyak4:47 13 May '08  
GeneralRe: Binding Preserving PinmemberLiveUltimate9:59 13 May '08  
QuestionRe: Binding Preserving [modified] Pinmembercivanovici4:54 30 May '08  

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.

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120517.1 | Last Updated 1 Nov 2007
Article Copyright 2007 by boris_richter
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid