保留绑定的 XAML 序列化器






4.33/5 (3投票s)
使用 MarkupWriter 保存 XAML 文件。
引言
不幸的是,XamlWriter 无法保留绑定(有关更多详细信息,请参见 XamlWriter.Save 的序列化限制)。我为此已经 Googled 了好几天,但还没有找到任何解决方案(如果有人已经有解决方案,请告诉我)。
因此,我创建了 XamlSerializer
类,它可以保存 Bindings。
背景
在 Googling 的过程中,我发现可以通过使用 MarkupWriter.GetMarkupObjectFor 来获取有关如何序列化对象的一些信息;它会为提供的对象返回一个 MarkupObject。对于对象中应该被序列化的所有属性,MarkupObject 在其 MarkupObject.Properties 集合中提供了 MarkupProperty。
通过使用根对象的 MarkupObject,可以深入了解对象的属性和包含的对象。
它是如何工作的?
有了这些信息,我已经开始实现 XamlSerializer
,它能够编写 XAML 并存储属性绑定。由于 XamlReader 在理解绑定方面没有困难,因此 XamlSerializer
使用 XamlReader.Load 来加载 XAML。
在 XAML 中,每个对象都由一个具有对象类型名称的标签表示;属性要么存储为特性,要么也存储为标签(复合属性)。对于内容属性(在包含类上由 ContentProperty 属性指定),只存储其值。因此,XamlSerializer
使用 XmlDocument 来创建 XAML,因为这样就不必在存储属性之前对它们进行排序(先属性,然后是复合属性的元素,最后是内容属性的值),而是可以按照 MarkupObject.Properties 返回的顺序存储,只需添加一个属性或追加一个子元素即可。
对象保存是通过 SaveObject
方法完成的,该方法需要父 XmlElement 和一个合适的 MarkupObject。
private void SaveObject(XmlElement parent, MarkupObject markupObject)
SaveObject
为 MarkupObject.Properties 返回的所有属性调用 SaveProperty
,并将对象的 XmlElement、MarkupObject 和相应的 MarkupProperty 传递给它。
private void SaveProperty
(XmlElement parent, MarkupObject markupObject, MarkupProperty property)
SaveProperty
首先检查它是否是 依赖属性 (MarkupProperty.DependencyProperty)。如果是,则调用 BindingOperations.GetBinding 来获取该属性的 Binding。如果存在 Binding,则该属性将作为元素存储,检索到的 Binding 使用通过调用 MarkupWriter.GetMarkupObjectFor 获得的 MarkupObject 进行存储,该 MarkupObject 是通过该 Binding 获得的。绑定的属性在 SaveBindingProperty
中存储。
private void SaveBindingProperty
(XmlElement parent, MarkupProperty property, Binding binding)
如果该属性不是 依赖属性,则检查它是否是对象的 content property,然后只使用 SavePropertyContent
存储其内容。
private void SavePropertyContent(XmlElement parent, MarkupProperty property)
复合属性使用 SaveCompositeProperty
中的 XmlElement 进行存储,该方法调用 SavePropertyContent
来存储属性的值。
private void SaveCompositeProperty(XmlElement parent, MarkupProperty property)
最后,可以作为 string
s 存储在属性中的属性由 SaveAttributeProperty
保存。
private void SaveAttributeProperty(XmlElement parent, MarkupProperty property)
命名空间
对于所有不包含在根对象 XAML 的 namespace
中的对象和属性,XAML 会期望适当的前缀和 URI 定义。这些由 GetNamespacePrefix
和 GetNamespaceUri
生成。这两个方法都期望一个 Type 来检索 namespace
和程序集。
private string GetNamespacePrefix(Type type)
private string GetNamespaceUri(Type type)
对于每个 namespace
/assembly 组合,一个前缀和一个 URI 定义(形式为 xmlns:PREFIX="clr-namespace:NAMESPACE;assembly=ASSEMBLY"
)将被添加到 XmlDocument 的 DocumentElement 中。对于来自 PresentationFramework
程序集的 namespace
s,使用 URI http://schemas.microsoft.com/winfx/2006/xaml/presentation。前缀存储在 Dictionary 中,以避免重复定义。
Using the Code
我使用 Microsoft Visual C# 2008 Express Edition (.NET 3.5) 创建了这个项目;我不知道它是否能在 .NET 3.0 上运行。
使用 XamlSerializer
非常简单:它提供了一些 static
方法来加载和保存 XAML。要加载存储在 XAML 中的对象,可以使用 Load
;保存则是使用 Save
(我想这是显而易见的)。
//==========================================================================
/// <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);
摘要
尽管仍然存在许多问题和 bug,但 XamlSerializer
现在使得在将对象序列化到 XAML 时能够保留绑定。我只对简单的 XAML 文件进行了一些基本测试,我相信有很多场景 XamlSerializer
仍然无法处理;但它将是一个很好的起点。
历史
- 2007-11-01:文章提交