XamlWriter.Save() 没有序列化 DependencyProperties
XamlWriter.Save() is not serializing DependencyProperties
从我的 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 中的元素。
XamlWriter.Save
似乎只序列化 本地 -set 值。在 XAML 中,值可以来自 multiple levels of sources.
当您直接设置 TextBlock.Text
时,您正在查看 "local value" 集(优先级 3)。但是,当您在数据模板中设置它时,您正在设置模板属性(优先级 4)。通过写作
textBlock.Text = textBlock.Text;
您实际上是在将其转换为本地 属性 集合(优先级 3)!
如果您查看 some of the source code involved in XamlWriter.Save, you can see (line 819),它会显式读取 属性.
的本地值
不幸的是,我不确定对此有什么好的解决方法。 XamlWriter 有 known limitations. You an try inheriting from XamlDesignerSerializationManager
and calling the XamlWriter.Save(Object, XamlDesignerSerializationManager)
重载,但看起来不太有前途。更有可能的是,您将不得不执行上述操作,或者编写您自己的序列化例程(至少 Microsoft 已将其源代码作为指南提供)。
根据 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
属性。
从我的 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 中的元素。
XamlWriter.Save
似乎只序列化 本地 -set 值。在 XAML 中,值可以来自 multiple levels of sources.
当您直接设置 TextBlock.Text
时,您正在查看 "local value" 集(优先级 3)。但是,当您在数据模板中设置它时,您正在设置模板属性(优先级 4)。通过写作
textBlock.Text = textBlock.Text;
您实际上是在将其转换为本地 属性 集合(优先级 3)!
如果您查看 some of the source code involved in XamlWriter.Save, you can see (line 819),它会显式读取 属性.
的本地值不幸的是,我不确定对此有什么好的解决方法。 XamlWriter 有 known limitations. You an try inheriting from XamlDesignerSerializationManager
and calling the XamlWriter.Save(Object, XamlDesignerSerializationManager)
重载,但看起来不太有前途。更有可能的是,您将不得不执行上述操作,或者编写您自己的序列化例程(至少 Microsoft 已将其源代码作为指南提供)。
根据 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
属性。