Building flexible Silverlight controls

When do you need it?

The most obvious thing to do in Silverlight and in WPF, when it comes to creating controls, is to grab a UserControl out of your toolbox. You can find it as a template when adding a new item to your project. It’s easy, but if it is the right template depends on what you want to do with it.
Questions you have to ask yourself before adding a UserControl are…

  • Do I need to reuse this control in other projects?
  • Will I have multiple instances in my app with a slightly different look-and-feel?
  • Does a designer need to work with this control, before or after compilation?

If the answer is yes to one or more of the questions above, then you need another solution than the out-of-the-box UserControl. The reason for this has to do with the level of separation between markup and code. With a standard UserControl you will get two files (.xaml and .cs/.vb) which are linked to each other visually in Visual Studio, but also during compile time. Together they become one thing. Both the XAML and the C#/VB code will be compiled into IL and merged during compilation (they’re partials). So the separation of markup and logic is only in design-time.
If you read the above questions again you will notice that it can be replaced by one question…

  • Do you want to maintain separation between markup and code beyond design-time?

An alternative, and more flexible, way of building controls is by leveraging the power of templating. With templating you get a real separation between markup and code, in theory. In practice there is separation, but also a dependency. The template is depending on a control created from code and in quite a lot of cases the code is dependent on some things that have to be in the template in order for the control to work.
I will start by showing the minimal approach to templated controls.

Creating a control class

Let’s first create a new class in Visual Studio. The new class will inherit from ContentControl. This is important! As far as I know you need to inherit from ContentControl in order to be able to assign a separate template to your control.

For the control to get its default template there is one line of code that we need to add to the constructor of our control and that is the following…

public class ExampleControl : ContentControl
{
    public ExampleControl()
    {
        // The following line of code binds this class
        // to a default Style for a specific type.
        // When using inheritance be aware of this.
        // Use GetType() to make it more dynamic
        DefaultStyleKey = typeof(ExampleControl);
    }
}

The DefaultStyleKey is used by the framework to determine if there is a default style registered as a resource for this control. It is not mandatory to have a default style registered even with the DefaultStyleKey set. It will not crash. If there is no style, then there is no style.

Creating a default style

The next step is to create the default style or template. Maybe in a future article I will go deeper into styles and templates, for now I keep it functional.

Depending on the kind of  project type your control is in, do the following…

  • In case of having the control in the main Silverlight Application (not preferred) you can place the default style into the App.xaml.
  • If the control is in a separate class library, which I recommend doing as the preferred way for reusing controls, then create a new folder in the root of your project called Themes (same casing) and add a new resource dictionary named generic.xaml (same casing) to this folder. The naming is important here, because it is a convention.

With the generic.xaml or App.xaml open, add the following xaml…

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:ExampleApplication.ControlLibrary"
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">

    <!-- Default style for the ExampleControl -->
    <Style TargetType="controls:ExampleControl">
        <!-- Put your properties here -->
        <Setter Property="Template">
            <Setter.Value>
                <!-- Default template for the ExampleControl -->
                <ControlTemplate TargetType="ContentControl">
                    <!-- Put your layout here -->
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

What you see above is an empty default style snippet. The important things to notice are…

  • The ResourceDictionary contains a custom namespace called xmlns:controls which is associated with the namespace of our control class.
  • The Style element points with its TargetType to our control class. Notice that there is no x:Key for this style, because it is the default style.
  • The style contains a Property Setter for the property Template of our control. So we set the Template through the Style. Warning! I have tried to split up the template and the style by giving the template an x:key and refer to it from the style, but this will not work the same way as having the template included in the style. In this case the styled template will not show up at run time.
  • The ControlTemplate element has a TargetType that points to ContentControl. You might think that it should point to our control type and even though it makes more sense, this idea will throw exceptions at runtime. I paid a night of debugging for it.

There are two places in this style where you can customize the look-and-feel of the control.

  1. Add more Property Setters for assigning values to properties of your control.
  2. Add a new hierarchy of controls in the Control Template part of the style.

This concludes the basics of building your own templated control by splitting code from design. There are enough challenges left, like template binding, data binding, etc… but you’ve made your first step in the right direction.

Advertisements

About this entry