Template parts and custom controls (quick tip)
April 1, 2010
Here’s a few simple tips and recommendations when developing custom Silverlight controls, as it relates to template parts.
What’s with all the control development tips?
We’re looking at the best way to share a lot of the ‘black magic’ of control development with the community, and will have details on that another way – today so much of this information is hidden away in blog posts like this one.
Hopefully we’ll have a whitepaper at some point.
What are template parts?
Visualizing a control and it’s expected template: “MyControl” has a template part called Button, of type ButtonBase. And although it also may contain a text box, no events from the text box are used. Instead, just a template binding to the Text property.
This control has 1 template part: Button, not two. Don’t be tempted to have a text box template part!
Do you need template parts or not?
It’s important to minimize the number of template parts in a control to reduce complexity. Over-engineering controls leads to reduced code quality, expensive control maintenance, and increased testing cost.
If you can expose dependency properties and template bind into those properties from controls in the template, there is absolutely no reason to have parts present.
If you need to hook up to events of anything in the template, it’s likely that you will need to have a part for that sub control.
Choose wisely.
Use constants for the names
To prevent potential spelling errors or simple mistakes, it’s a good idea to not hard-code the template part name anywhere in your control.
Instead, add a constant string to the top of the file with the part name. You can then refer to it in the OnApplyTemplate method, the template part attribute, and anywhere else it is needed.
The constant can be protected if a subclass may want to reuse the name.
private const string PartButtonName = "Button";
Store parts as private fields in your class
Template parts are not designed to always be inherited by any subclasses. Though the subclasses are welcome to use the same parts, they should not expect to just use the base classes: they need to use OnApplyTemplate to also grab instances of the parts.
So do use private fields to store the parts in the OnApplyTemplate method:
private ButtonBase _button;
Detach any existing event handlers in OnApplyTemplate
Controls can be restyled and retemplated as a result. Make sure to detach any event handlers from previous template parts before adding new handlers.
Here’s the typical pattern for that:
public override void OnApplyTemplate() { base.OnApplyTemplate(); if (_button != null) { _button.Click -= OnButtonClick; } _button = GetTemplateChild(PartButtonName) as ButtonBase; if (_button != null) { _button.Click += OnButtonClick; } }
Don’t assume template parts exist
Whenever using template parts in your code, you should not assume that they exist. Always check for null, and unless a significant reason exists for requiring their presence, never throw an error in this condition.
Often code in the class relating to states and properties will execute long before the template is applied, yielding no template part instances early on.
if (_button != null) { // Do something with the button }
Mark controls with template part attributes
Design surfaces like Expression Blend expect that controls are marked with the template part attributes. These are required to provide helpful tooling for editing templates.
Make sure to use the constant template part name that you created a few tips ago:
[TemplatePart(Name = PartButtonName, Type = typeof(ButtonBase))] public partial class MyControl : ContentControl { }
Template parts don’t necessarily inherit from their base class
It’s possible to have different template parts at each level of a control’s hierarchy. It’s even possible that subclasses may not want to use a template of a base class.
As a result, never provide template parts as public or protected properties on your controls. If subclasses want them, they can ask for them.
This is another reason it’s important to quietly check template parts for null, they may not be defined in base classes.
You should define template part attributes on every subclasses where expected. They do not inherit.
Hope this helps.
Other tips
David Anson has been furiously writing tips as well as we all have hallway conversations on control development: Check out: