Consider the following XAML from my UserControl:
<TextBlock Text="HelloWorld" Loaded="TextBlock_OnLoaded" />
And the associated event handler:
private void TextBlock_OnLoaded(object sender, RoutedEventArgs e)
{
var xaml = XamlWriter.Save(sender);
Console.WriteLine(xaml);
}
When the TextBlock is loaded, the following output is written to the Console:
<TextBlock Text="HelloWorld" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
Now consider this alternative XAML:
<ListBox ItemsSource="{Binding SomeCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="HelloWorld" Loaded="TextBlock_OnLoaded" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now when the TextBlock is loaded, the following output is written to the Console:
<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" />
......
Notice that the TextProperty is no longer being serialized.
If the following TextProperty assignment is added before the call to XamlWriter.Save():
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);
}
Then when the TextBlock is loaded, the following output is written to the Console:
<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" />
......
Notice that the TextProperty is again being serialized.
This blog post explains that "... if the property is backed by a DependencyProperty ... the property is only written if it is actually set."
It appears that the TextProperty is indeed being set in the first usage example, but not in the second usage example with the ListBox and DataTemplate.
Can anyone explain why this is the case, and how to overcome this obstacle?
My best guess is that the XAML parser is somehow setting the TextBlock state internally instead of calling SetValue on the dependency property, but I'm not sure why it would do this only for elements inside a DataTemplate.
XamlWriter.Save
appears to only serialize locally-set values. In XAML, values can come from multiple levels of sources.
When you set TextBlock.Text
directly, you are looking at a "local value" set (precedence 3). However, when you set it inside a data template, you are setting template properties (precedence 4). By writing
textBlock.Text = textBlock.Text;
you are actually transforming that into a local property set (precedence 3)!
If you look at the some of the source code involved in XamlWriter.Save, you can see (line 819) that it explicitly reads the local value of the property.
Unfortunately, I'm not sure what a good work-around is for this. XamlWriter has known limitations. You an try inheriting from XamlDesignerSerializationManager
and calling the XamlWriter.Save(Object, XamlDesignerSerializationManager)
overload, but it doesn't look very promising. More likely, you will have to either do what you do above, or write your own serialization routine (at least Microsoft has made their source readily available as a guide).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With