January 17, 2010

Silverlight Bits&Pieces: Derived Custom Controls

OK, let’s put the last findings to good use and create a derived control that carries its own default template. This post is again about some fairly basic stuff, but it is the logical next step.

My use case: I wanted/needed a simple image button, one that simply takes the image as a property, rather than having to manipulate the content for every button anew. So what do I need?

A derived class.

A dependency property for the image URL.

A new default template.

Deriving a SL control is just a matter of clicking add/new item in the solution explorer, and choosing „Silverlight Templated Control“. This will actually create 2 things (and address the default template requirement as well…):

A class derived form Control, placed in a .cs file in the folder I used to create the new item.

A XAML file named Themes/Generic.xaml is created (or extended if it already exists) and contains a style and template for the new control.

Now, the implied behavior is that a custom control sets its DefaultStyleKey property to a type (usually its own). At runtime SL will determine the default style of a control by using this type’s assembly to read the Themes/Generic.xaml content and pick the style that has the type as target type.

Note how the style in Themes/Generic.xaml uses an XML namespace to map the class name to a C# namespace:

Note: One consequence of this is that all styles of all custom controls in an assembly will end up in the same generic.xaml file. This is usually not an issue, even if implementation and style/template reside in relatively remote files. However, if the assembly grows to accommodate a bigger number of controls, it might make sense to put the control specific resources into separate .xaml files right beside the implementation. Loading the template from a .xaml resource is no big deal, all you need is GetManifestResourceStreamand XamlReader.Load, in more detail here.

The next step is to change the base class — I want to extend the Button, not write it completely anew — and to provide the dependency property for the image. Having a peek at the Sourceproperty of the Image control tells me that ImageSource is the adequate type.

Now, let’s customize the appearance. Unfortunately Blend cannot deduce the dependency between the control an the style in Themes/Generic.xaml. Therefore its easier to create an instance of the ImageButton, assign a temporary style with template in blend, placed it into the same page’s XAML:

and the respective button:

Of course I need to have the respective image…

I can now use Blend to work on the template (assigned within the style):

This will change to editing context to the template rather than the control:

I changed it to include the image, placed it beside the text. (Actually, placed beside what I choose to have in the content property, using a content presenter.)

Now I want the image control to show what I have in the NormalImage property I just wrote. Blend is aware of the type of my class, so I can bind the Image.Source property using a template binding to the property of my class.

and clicking it:

The temporary style with template finally looks like this:

Template bindings can be used to bind against existing properties (of appropriate type), as well as any new property I choose to provide. Actually for a complete implementation I would probably have to map alignments, and other properties accordingly to parts of my template, to provide full customizability for my control.

Now that I’m done designing my button, I can save it, copy the resulting template into the default style for my control in Themes/Generic.xaml, recompile – and then just use it:

Just an image, no text; and at runtime:

Alright, that’s the basics of a custom control. Essentially what I’ve done is

replacing the default template with one that includes an image

providing a dependency property for the image, actually nothing more than a mirror of the respective property of the image in my template.

This is all very boilerplate on one hand, yet extremely flexible at the same time.

Now, the image of the button does not „feel“ very buttonish, i.e. it does not reflect mouse over, disabled state, or clicked state. This is the domain of VSM. Next post…