XamlWriter.Save()没有序列化DependencyProperties

时间:2015-01-21 23:48:45

标签: c# wpf xaml xamlwriter uielementcollection

从我的UserControl考虑以下XAML:

<TextBlock Text="HelloWorld" Loaded="TextBlock_OnLoaded" />

相关的事件处理程序:

private void TextBlock_OnLoaded(object sender, RoutedEventArgs e)
{
    var xaml = XamlWriter.Save(sender);
    Console.WriteLine(xaml);
}

加载TextBlock后,以下输出将写入控制台:

<TextBlock Text="HelloWorld" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />

现在考虑这个替代XAML:

<ListBox ItemsSource="{Binding SomeCollection}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="HelloWorld" Loaded="TextBlock_OnLoaded" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

现在加载TextBlock时,以下输出将写入控制台:

<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
......

请注意,TextProperty不再被序列化。

如果在调用XamlWriter.Save()之前添加了以下TextProperty赋值:

private void TextBlock_OnLoaded(object sender, RoutedEventArgs e)
{
    var textBlock = sender as TextBlock;
    if (textBlock != null)
    {
        textBlock.Text = textBlock.Text;
    }

    var xaml = XamlWriter.Save(sender);
    Console.WriteLine(xaml);
}

然后加载TextBlock时,以下输出将写入控制台:

<TextBlock Text="HelloWorld" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
<TextBlock Text="HelloWorld" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
<TextBlock Text="HelloWorld" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
......

请注意,TextProperty再次被序列化。

这个blog post解释说“......如果属性由DependencyProperty支持......只有在实际设置属性时才写入属性。”

看起来TextProperty确实是在第一个用法示例中设置的,但在第二个使用示例中没有设置ListBox和DataTemplate。

任何人都可以解释为什么会这样,以及如何克服这个障碍?

我最好的猜测是XAML解析器以某种方式在内部设置TextBlock状态而不是在依赖属性上调用SetValue,但我不确定为什么它只会对DataTemplate中的元素执行此操作。

2 个答案:

答案 0 :(得分:1)

XamlWriter.Save似乎只序列化 本地 - 设置值。在XAML中,值可以来自multiple levels of sources

当您直接设置TextBlock.Text时,您正在查看&#34;本地值&#34;设置(优先级3)。但是,在数据模板中设置它时,您将设置模板属性(优先级4)。通过写作

textBlock.Text = textBlock.Text;

您实际上正在将其转换为本地属性集(优先级3)!

如果查看some of the source code involved in XamlWriter.Save,您可以看到(line 819)它明确读取了该属性的本地值。

不幸的是,我不确定这是一个好的解决办法。 XamlWriter有known limitations。您尝试继承XamlDesignerSerializationManager并调用XamlWriter.Save(Object, XamlDesignerSerializationManager)重载,但它看起来并不乐观。更有可能的是,您必须要么完成上述操作,要么编写自己的序列化例程(至少Microsoft已经将其源代码作为指南提供)。

答案 1 :(得分:0)

根据NextInLine的答案,我已经做了以下工作:

public static IEnumerable<DependencyProperty> GetDependencyProperties(this DependencyObject obj)
{
    var propertyDescriptors = TypeDescriptor.GetProperties(obj, new Attribute[]
    {
        new PropertyFilterAttribute(PropertyFilterOptions.All)
    });

    return (from PropertyDescriptor pd in propertyDescriptors
            let dpd = DependencyPropertyDescriptor.FromProperty(pd)
            where dpd != null
            select dpd.DependencyProperty).ToList();
}

public static IEnumerable<DependencyProperty> GetUpdatedDependencyProperties(this DependencyObject obj)
{
    return (from property in obj.GetDependencyProperties().Where(x => !x.ReadOnly)
            let metaData = property.GetMetadata(obj.GetType())
            let defaultValue = metaData.DefaultValue
            let currentValue = obj.GetValue(property)
            where currentValue != defaultValue
            select property).ToList();
}

可以这样使用:

foreach (var updatedProperty in dependencyObject.GetUpdatedDependencyProperties())
{
    dependencyObject.SetValue(updatedProperty, dependencyObject.GetValue(updatedProperty));
}

这会强制XamlWriter.Save(dependencyObject)序列化在XAML中更新的所有dependencyObject属性。