从我的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中的元素执行此操作。
答案 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
属性。