9.11 XAML Readable Types
XAML is an XML format used by WPF (and other technologies) to represent object graphs. The following guidelines describe design considerations for ensuring that your types can be created using XAML readers.
CONSIDER providing the default constructor if you want a type to work with XAML.
For example, consider the following XAML markup:
<Person Name="John" Age="22" />
It is equivalent to the following C# code:
new Person() { Name = "John", Age = 22 };
Consequently, for this code to work, the Person class needs to have a default constructor. Markup extensions, discussed in the next guideline in this section, are an alternative way of enabling XAML.
DO provide markup extension if you want an immutable type to work with XAML readers.
Consider the following immutable type:
public class Person { public Person(string name, int age){ this.name = name; this.age = age; } public string Name { get { return name; } } public int Age { get { return age; } } string name; int age; }
Properties of such type cannot be set using XAML markup, because the reader does not know how to initialize the properties using the parameterized constructor. Markup extensions address the problem.
[MarkupExtensionReturnType(typeof(Person))] public class PersonExtension : MarkupExtension { public string Name { get; set; } public int Age { get; set; } public override object ProvideValue(IServiceProvider serviceProvider){ return new Person(this.Name,this.Age); } }
Keep in mind that immutable types cannot be written using XAML writers.
AVOID defining new type converters unless the conversion is natural and intuitive. In general, limit type converter usage to the ones already provided by the .NET Framework.
Type converters are used to convert a value from a string to the appropriate type. They're used by XAML infrastructure and in other places, such as graphical designers. For example, the string "#FFFF0000" in the following markup gets converted to an instance of a red Brush thanks to the type converter associated with the Rectangle.Fill property.
<Rectangle Fill="#FFFF0000"/>
But type converters can be defined too liberally. For example, the Brush type converter should not support specifying gradient brushes, as shown in the following hypothetical example.
<Rectangle Fill="HorizontalGradient White Red" />
Such converters define new "minilanguages," which add complexity to the system.
CONSIDER applying the ContentPropertyAttribute to enable convenient XAML syntax for the most commonly used property.
[ContentProperty("Image")] public class Button { public object Image { get; set; } }
The following XAML syntax would work without the attribute:
<Button> <Button.Image> <Image Source="foo.jpg" </Button.Image> </Button>
The attribute makes the following much more readable syntax possible.
<Button> <Image Source="foo.jpg" </Button>