Home > Patterns > Coding for XAML

Coding for XAML


XAML is basically a way of describing object structures, which will be instantiated by the XAML parser when a page, resource or template is used. Since one of the ideas in WPFGlue is to configure components in XAML, I had to wrestle a bit with the VS XAML designer and my classes in order to make them work well with each other. This post is about what patterns you can use in order to make classes nice to use in XAML, and how to implement them.

Basics

A XAML tag is basically nothing but a class name and some attributes. The class name stands for the component, the attributes, for its properties. What the XAML parser will do is to create an instance of the class using a parameterless constructor, and to set the properties which have the same names as the attributes to the provided attribute values. So, the basic rules for creating a class for use in XAML are:

  • The class must be public.
  • It must have a public, parameterless constructor.
  • All properties that will be accessed in XAML must be public.

Two other best practices turned out to influence the XAML experience of my custom components, even though I didn’t quite find out why, and I expect this behaviour to change with version 4.0… Well, the problem was that the VS 2008 XAML editor didn’t recognize collection types properly, and that its Intellisense function would suggest every available element, not only the one which are allowed in the collection.

In order to fix that, I did the following:

    Using the ContentProperty Attribute

    XAML elements can have one or many content elements. A content element is something like the “natural” or most important child element of its parent. It is special because you can set it in XAML without mentioning the name of the property which references it. However, in order to create an object structure, the XAML parser needs to know the property name. Therefore, you can set the name of the property which holds the child element by adding the System.Windows.Markup.ContentProperty(“MyChild”) attribute to the class declaration, where “MyChild” would be the name of the property which holds the child.

    If the property is of an collection type, the element can contain multiple child elements, and they will all be added to the collection which the property holds.

    Collections for XAML

    If you set up your project like I explained in the first section, all you have to do is to derive a collection class from List(Of T) or ObservableCollection(Of T), where T is your item type.

    Public Class XamlCollection
        Inherits List(Of XamlCollectionItem)

    End Class

    Public Class XamlCollectionItem

        Private _IntegerValue As Integer
        Public Property IntegerValue() As Integer
            Get
                Return _IntegerValue
            End Get
            Set(ByVal value As Integer)
                _IntegerValue = value
            End Set
        End Property

    End Class

    If you do it this way, the XAML will display and compile without problems, and Intellisense will offer you only the available classes of the correct type. Normally, since we are talking about configuring components through XAML, the collection is not expected to change during run time, so it would be enough to use List(Of T).

    If you want to use a collection type as the content for an element, you’d have to do the following further steps:

    • Create a read-only property which holds an instance of the collection type.
    • Initialize it with an empty collection in the elements constructor.
    • Set the ContentProperty attribute to the property’s name.
    <System.Windows.Markup.ContentProperty("Children")> _
    Public Class XamlCollectionParent
        Private _Children As New XamlCollection
        Public ReadOnly Property Children() As XamlCollection
            Get
                Return _Children
            End Get
        End Property
    End Class

    Referring to Objects by Name I: Keyed Collections

    Sometimes you want to refer to be able to refer to specific children of an element. The best way to make sure that you get the correct child is to name the children and to refer to them by name. In XAML, you would do this in a binding’s property path, like this: {Binding Path=MyParentElement.Children[ChildName]}

    In order to be able to do that, you’d derive the collection class from KeyedCollection(Of T). Then you’d have to override the GetKeyForItem function, which is supposed to create a unique string key from an item of type T.

    Public Class XamlDictionary
        Inherits System.Collections.ObjectModel.KeyedCollection(Of String, XamlDictionaryItem)

        Protected Overrides Function GetKeyForItem(ByVal item As XamlDictionaryItem) As String
            Return item.Key
        End Function
    End Class

    Public Class XamlDictionaryItem
        Private _Key As String
        Public Property Key() As String
            Get
                Return _Key
            End Get
            Set(ByVal value As String)
                _Key = value
            End Set
        End Property

        Private _IntegerValue As Integer
        Public Property IntegerValue() As Integer
            Get
                Return _IntegerValue
            End Get
            Set(ByVal value As Integer)
                _IntegerValue = value
            End Set
        End Property
    End Class

    You may ask why I don’t use Dictionary(Of TKey, TValue). Well, it seems that XAML’s support for dictionaries is very much geared towards the special needs of ResourceDictionaries, and I believe it is not intended to be used generally. However, KeyedCollection offers you the additional benefit that you can simply iterate over the items and don’t have to worry about mapping Key / Value pairs, since the keys are taken directly from the items.

    Referring to Objects by Name II: NameScopes

    A NameScope in WPF is what makes element names in a page unique and allows to reference elements by name using their x:Name attribute. If you have a tree structure of elements, but need to reference single elements by name also, a NameScope is the thing to use.

    A Namescope is defined through a root element XAML. All descendants of this element which have an x:Name attribute will automatically be registered to the Namescope. In order for this to work, you have to do the following:

    • The class which is the root element has to implement the System.Windows.Markup.INameScope interface. Internally, the implementation can be based on the NameScope class, which comes with the WPF framework.
    • The classes which are used as descendants of the root element need to use the System.Windows.Markup.RuntimeNameProperty attribute in their declaration, so that the XAML parser knows which property of the class stores the name.
    • In order to access the items by name, one needs an indexer. XAML is a little bit limited in its support for indexed properties: you have to have a class which declares the indexer as its default property. In order to provide indexed access to the named elements, this examples uses a nested class which does nothing but find the elements through its parent’s NameScope.
    Private _Indexer As New NamescopeIndexer(Me)
    Public ReadOnly Property Node() As NamescopeIndexer
        Get
            Return _Indexer
        End Get
    End Property

    Public Class NamescopeIndexer
        Private _Root As XamlNamescopeRoot
        Friend Sub New(ByVal root As XamlNamescopeRoot)
            MyBase.New()
            _Root = root
        End Sub

        Default Public ReadOnly Property Item(ByVal name As String) As NamescopeNode
            Get
                Return _Root.Namescope.FindName(name)
            End Get
        End Property
    End Class

     

    Markup Extensions

    Markup extensions are helpful in situations where you have to do more complicated things than just creating an instance of a class and assign it some values. For example, you can use a Markup extension if you want to establish a binding, or if you want to access a backing store which itself is not accessible to XAML, like application settings, localized resources or a configuration file.

    However, I am not very fond of markup extensions. They are basically a convenience: normally, you can achieve the same using just a more verbose syntax. And the convenience comes at a price:

    Markup extensions have to execute well both at runtime and in the VS designer. Both environments are radically different, and it is not trivial to find out the difference from inside the markup extension. Also, if there is any problem in design mode, there is no debugging support, since the designer always uses the release compiled version of the markup extension. Moreover, I even couldn’t use structured error handling inside the class, since my catch blocks never were called, and sometimes VS crashes when compiling or editing markup extensions.

    Anyway, for the sake of completeness, here what I learned:

    • Derive a markup extension from System.Windows.Markup.MarkupExtension and override the ProvideValue method.
    • Set the MarkupExtensionReturnType attribute to the correct type.
    • If the type of the serviceProvider parameter is System.Windows.Markup.ProvideValueServiceProvider, you are in runtime mode, otherwise, you are in design mode.
    • Use serviceProvider.GetService(GetType(IProvideValueTarget)) in order to retrieve information about the property which will receive the value. If the TargetProperty parameter is of type DependencyProperty, the property is a dependency property and you can do things like set up a binding, so that the value will be updated automatically if the underlying data source changes.
    • Always return a valid value of the correct type, even if the value is some message like “I couldn’t find what you wanted”.
    • Whatever you do, avoid exceptions in the ProvideValue method at all costs. In this case this doesn’t mean “catch”, but really “avoid”, since structured exception handling doesn’t seem to work when the extension is used in the designer.

    Reporting Configuration Errors

    As a rule, your component shouldn’t raise any exceptions if it doesn’t have all the information it needs to do its job, but just make do without it, using intelligent default behaviour or gracefully reducing the functionality. However, sometimes you just have to report something.

    In this case, you can raise an ArgumentException with an appropriate message in the property setter which receives the invalid value. Exceptions that are raised in this way will show up in the VS Error List and keep the project from building.

    Other Best Practices

    • Don’t do complex things like e.g. accessing a database in a constructor. Constructors will run at design time as well as at run time. So, if they need information from the application they are running in, this information might not be accessible at design time. Therefore, one might see strange error messages at design time which are difficult to debug.
    • Don’t rely on a certain order in which the properties of your component should be set. If you need several property values in order to do a job, either don’t access the values until you need to do the job, or check whether you have a complete set of values each time any of the properties changes, and start the job as soon as you have a valid set of values.

    Conclusion

    If you follow these guidelines, your components will behave similar to the components that come with the WPF framework, and they will integrate into the support given by the VS XAML editor.

    There are some other techniques which I haven’t covered here, but which I would like to mention: using dependency properties and using type converters. You can find examples for dependency properties all over this blog, e.g. here: https://wpfglue.wordpress.com/2009/12/16/the-sticky-viewmodel-pattern/ . An example for using type converters is given in this post: https://wpfglue.wordpress.com/2009/11/30/css-class-equivalent-in-wpf/ . Otherwise, you will find the patterns which I discussed here everywhere in the WPFGlue code. A special example project for this post is available for download here:

    (as always on wordpress.com, you will have to rename it to a .zip extension in order to extract it).

    Advertisements
    Categories: Patterns
    1. No comments yet.
    1. May 23, 2010 at 17:48

    Leave a Reply

    Fill in your details below or click an icon to log in:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s

    %d bloggers like this: