Xaml serialization quirks and gotchas
I recently had to build a little tool that would read its configuration from a XAML file (because XAML serialization is, most of the time, better and more customizable than standard XML serialization). The trick was that this tool had to be built on top of .NET 3.0 – not 3.5 or 4.0. And I discovered that there are a few little gotchas in .NET 3.0’s XAML serializer that I, somehow, never ran into before.
Gotcha #1: Automatic properties
Let’s say I wanted to deserialize the following class:
1: public class Foo
2: {
3: [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
4: public IList<int> Values { get; private set; }
5:
6: public Foo()
7: {
8: Values = new List<int>();
9: }
10: }
Pretty straightforward… however, if I try to deserialize it from a XAML file, I get the following exception:
property ‘Foo.Values’ cannot be set because it does not have an accessible set accessor. Line ‘3’ Position ‘4’.
I thought the framework was high on drugs or something, as what I wrote was pretty much the main use-case for the DesignerSerializationVisibility attribute. But then I remembered this other little gotcha with property reflection: when you use the auto-property syntax from C# 3.0 for a read-only property, the PropertyInfo you get on that property says you can write to it. This is because there is a setter method – only it’s private. And that’s why the XAML serializer complains about accessibility.
Note that this has been fixed with the new XAML serialization in .NET 4.0, with the new System.Xaml namespace. But if you need to target a previous version of the framework, you’ll need to refactor your code like so:
1: public class Foo
2: {
3: private List<int> mValues = new List<int>();
4:
5: [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
6: public IList<int> Values
7: {
8: get { return mValues; }
9: }
10: public Foo()
11: {
12: }
13: }
Gotcha #2: Public interface
Now you may think that the XAML serializer would be happy, but no… this is a very picky serializer! The next exception you get is:
‘Values’ is a read-only IEnumerable property, so ‘ConsoleApplication2.Foo’ must implement IAddChild. Line ‘3’ Position ‘4’.
That’s obviously weird, because the property is actually a list. But my guess is that the serializer is only looking for the ICollection interface. And the problem is that IList<T> does not implement that interface – it implements ICollection<T>, sure, but neither of those generic interfaces implement the “legacy” interfaces. That why most of the time you’re better off writing a proper collection class that inherits from Collection<T>, or some other base class from the System.Collections.ObjectModel namespace, because they implement both the generic and “legacy” interfaces… but sometimes you may not feel like writing an “IntCollection” class, right? Well. Life sucks.
Oh, and don’t bother actually implementing the IAddChild interface, or making the list itself implement that interface, it’s useless, you’ll get the same exception. That interface is some kind of weird leftover from the beta phase of WPF, and is now half-deprecated, half-internalized, as mentioned on that MSDN forums thread.
So basically, in our case, a quick fix would be changing the public interface to a List<int>.
After those few changes, the XAML serializer is happy and running fine!
Hope this helps.