XAML and Custom Classes
XAML supports the ability to define a custom class or structure in any common language runtime (CLR) language, and then access that class using XAML markup. This topic discusses the requirements that a custom class must satisfy to be usable as a XAML object element in Silverlight.
This topic contains the following sections.
- Custom Classes in Applications or Assemblies
- Requirements for a Custom Class as a XAML Object Element
- Requirements for Properties of a Custom Class as XAML Attributes
- Requirements for XAML Event Handler Attribute Syntax on Events of a Custom Class
- Collection Properties
- Declaring XAML Content Properties
- Custom Attached Properties
- Related Topics
Custom classes that are used in XAML can be defined in two distinct ways: within the code-behind that is packaged with the primary Silverlight-based application, or as a class in a separate assembly, such as an executable or DLL used as a class library. Each of these approaches has particular advantages and disadvantages:
The advantage of creating a class library is that classes in a library can be shared and repackaged for use by many different applications. A separate library also makes versioning issues easier to control, and it makes it possible to create a class that you intend to use as a root element on a XAML page.
The advantage of defining the custom classes in the application is that the technique is relatively lightweight. Classes defined in the same assembly will minimize deployment and testing problems you might encounter when you introduce external assembly dependencies to your Silverlight project. However, one notable disadvantage is that you cannot use your custom class as a root element. You must either reference your custom class in a different assembly or limit your derived class uses to the UserControl partial class that enables code-behind in the basic Silverlight managed project structure.
Whether defined in the same assembly or a different assembly, custom classes must be mapped by CLR namespace and XAML namespace in order to be referenced in XAML. For more information, see Silverlight XAML Namespaces, and Mapping XAML Namespaces as Prefixes.
In order to be instantiated as an object element, your class must meet the following requirements:
The custom class must be public and support a default (parameterless) public constructor.
The custom class must not be a nested class (nested classes and the dot in their syntax interfere with other Silverlight and XAML features such as attached properties).
In addition to enabling object element syntax, you also enable property element syntax for any other writeable public properties that take your custom class as the property type. This is because the object can now be instantiated as an object element and can fill the property element value of such a property.
Structures that you define as custom types are always able to be constructed in XAML in Silverlight. This is because the CLR compilers implicitly create a default constructor for a structure that initializes all property values to their defaults. In some cases, the default construction behavior and/or object element usage for a structure is not desirable. This might be because the structure is intended to fill values and function conceptually as a union, where the values contained might have mutually exclusive interpretations and thus none of its properties are settable as separate attributes. A Silverlight example of such a structure is GridLength. Generally, such structures should implement a type converter such that the values can be expressed in attribute form, using string conventions that create the different interpretations or modes of the structure's values, and the structure should also expose similar behavior for code construction through a non-default constructor.
A custom property must meet at least one of the following requirements:
Uses a by-value primitive property type that the Silverlight XAML processor can natively convert, such as int, or use string.
Uses a by-reference class for property type that has a default constructor.
Uses a by-reference class for property type that has a dedicated type converter at the class level.
Has a type converter attributed at the property level.
Uses a property type that is an abstract class or interface, but the actual value is a practical class instance.
Abstract Classes and Interfaces
The property type can be an abstract class or an interface. At run time, the property value must be filled with an object that has implemented the interface or is an instance of a practical class that derives from the abstract class.
Properties can be declared on an abstract class and then set as attributes on a practical class that derives from the abstract class. The practical class must support the general object element requirements to enable usage of any of the inherited attributes.
Type-Converter Enabled Attribute Syntax
If you provide a dedicated, attributed type converter at the class level, the applied type conversion enables attribute syntax for any property that uses that type. A type converter does not enable object element usage of the type; only the presence of a default constructor for that type enables object element usage. Therefore, properties that are type-converter enabled are generally speaking not usable in property element syntax, unless the type itself also supports object element syntax. The exception to this is that you can specify a property element syntax, but have the property element contain a string. That usage is essentially equivalent to an attribute syntax usage, and such a usage is not common unless there is a need for more robust white space handling of the attribute value.
A case where attribute syntax is allowed but property element syntax that contains an object element is disallowed through XAML is properties that take the Cursor type.
Per-Property Type Converters
Alternatively, the property itself may declare a type converter at the property level. This enables a "mini language" that instantiates objects of the type of the property inline, by processing incoming string values of the attribute as input for a ConvertFrom operation based on the appropriate type. Typically, this is done to provide a convenience accessor, and not as the sole means to enable setting a property in XAML. However, it is also possible to use type converters for attributes where you want to use existing types that do not supply either a default constructor or an attributed type converter.
Whenever you expose a property that has a XAML usage, particularly if you are a control author, you should strongly consider backing that property with a dependency property. A dependency property will expose property system features for your property that users will come to expect for a XAML-accessible property. This includes features such as animation, data binding, and style support.
Writing and Attributing a Type Converter
Properties that take a collection type have a XAML syntax whereby objects declared in markup as child elements are added to the collection. This syntax has two notable features:
An element of the class that impements the collection behavior does not need to be specified in object element syntax. The presence of that collection type is implicit whenever you specify a property in XAML that takes a collection type.
Child elements of the collection property are processed to become members of the collection. Ordinarily, the code access to the members of a collection is performed through collection methods such as Add, or through a collection indexer property. XAML syntax does not support methods or indexers directly. Collections are obviously a very common requirement for building a tree of elements, and you need some way to populate these collections in declarative XAML. Therefore, child elements of a collection property in XAML are processed by adding them to the collection that is the collection property type value.
The Silverlight XAML processor uses the following definition for what constitutes a collection property. The property type of the collection property must satisfy one of the following:
Each of these types has an Add method, which is the entry point used by the XAML processor to add items to the underlying collection when a XAML collection property is parsed.
You can implement a custom collection type for your collection property. Because of implicit collection property treatment, the custom collection type does not need to provide a default constructor in order to be used in XAML implicitly. However, you can optionally provide a default constructor for the collection type. This has advantages and disadvantages.
The advantages are as follows:
If you have a constructor on the collection type, you can explicitly declare the collection as an object element and apply other properties such as x:Name values to the collection.
Some markup authors might prefer to see the explicit collection as a matter of markup style.
A default constructor can simplify the initialization requirements when you create new objects that use your collection type as a property value.
The notable disadvantage is that the general .NET Framework design guidelines encourage you to make all collection properties read-only. By this guideline, the owning class is always responsible for collection initialization, and callers of the collection can only adjust the items. An explicit object element for the collection property ultimately requires a writeable collection property because it initializes a new collection object in the object tree, by calling the collection class constructor. Thus the explicit object element usage is at odds with the read-only collection property design guidelines.
The XAML language defines the concept of a XAML content property. Each class that is usable in object syntax can have exactly one XAML content property. To declare a property to be the XAML content property for your class, apply the ContentPropertyAttribute as part of the class definition. Specify the name of the intended XAML content property as the ContentPropertyAttribute.Name in the attribute.
XAML content property definitions inherit to derived classes. A derived class can deliberately have a different named XAML content property if you apply the ContentPropertyAttribute again to the derived class.
You can specify a collection property to be the XAML content property. This results in a usage for that property whereby the object element can have one or more child elements, without any intervening collection object elements or property element tags. These elements are then treated as the value for the XAML content property and added to the backing collection instance.
Some existing Silverlight XAML content properties use the property type of Object. This enables a XAML content property that can take primitive values such as a String as well as taking a single reference object value. If you follow this model, your property-owning type is responsible for type determination as well as the handling of possible types. The typical reason for an Object type model is to support both a simple means of adding object content as a string (which receives a default presentation treatment) and an advanced means of adding object content that specifies a non-default presentation.
So long as you are targeting Silverlight 4 or later, the Silverlight XAML processor supports ISupportInitialize behavior for properties of a custom class. This is useful for special cases where properties of your class are order-dependent; by default, a XAML processor treats all properties as not being order-dependent, and you cannot rely on properties being set in the strict declaration order. When a type is initially processed, BeginInit is called by the Silverlight XAML processor. As a final loading step, EndInit is called. To enable this behavior and interaction with the XAML processor, your type must specifically implement ISupportInitialize. There is no inherent ISupportInitialize support in FrameworkElement or any other Silverlight base class (certain data binding classes do implement the interface, but that is an implementation detail that you should not rely upon).
The XAML language defines a concept of an attached property. For more information on attached properties, see Attached Properties Overview.
The class that defines an attached property is not required to support a XAML object element usage. Attached properties are defined by exposing a method accessor pattern that the Silverlight XAML parser and XAML parsers in general can recognize. An additional consideration is that custom attached properties in Silverlight are typically implemented with dependency property backing for the underlying property store. For more information, including the accessor pattern and registration API usage, see Custom Attached Properties.