When creating a control in WPF the aim is to try to separate the UI from the functionality that implements the control logic. That is to say, create a “lookless” control. A lookless control may potentially have one or more of the following: functionality, properties, events, styles and visual state. The control itself should not know about how it’s displayed but may well know what part’s it might wish to manipulate (and what base types they are).
We want to publish the parts of the control that may be manipulated by a ControlTemplate so that a designer application, such as Visual Studio’s built-in WPF UI designer or Expression Blend, can find out which parts the control manipulates or what styles are used or what states exist within the control.
The following attributes are applied to a control’s class definition to publish various pieces of metadata.
TemplatePart Attribute
A control may be thought of as broken up into sections, for example maybe we’ve created a colour picker type of control which has a section made up of a selection of rectangles each showing a colour and possibly another section which is some for of input (let’s assume a TextBox) which is used to take the hexadecimal representation of a colour.
Within our code we’d declare these parts as follows:
[TemplatePart(Name = PART_HexadecimalInput, Type = typeof(TextBox))] [TemplatePart(Name = PART_ColourSelectionPanel, Type = typeof(Panel))] public class ColourPicker : Control { private const string PART_HexadecimalInput = "PART_HexadecimalInput"; private const string PART_ColourSelectionPanel = "PART_ColourSelectionPanel"; // implementation }
Note: The general rule is to name such parts by prefixing with PART_
Now within our implementation of the control we will have some code to get the various parts from the ControlTemplate applied to this control. This is usually handled in the OnApplyTemplate method as per the following
public override void OnApplyTemplate() { base.OnApplyTemplate(); hexadecimalInput = GetTemplateChild(PART_HexadecimalInput) as TextBox; colourSelectionPanel = GetTemplateChild(PART_ColourSelectionPanel) as Panel; }
Assuming hexadecimalInput is of type TextBox and colourSelectionPanel is a Panel, we now have access to the parts that potentially make up the ControlTemplate. I say potentially because it’s equally possible that somebody re-templates to include only limited functionality, so for example maybe we just want the colour picker then we remove the PART_HexadecimalInput from our ControlTemplate. Thus this code should handle situations where a part is not found in the template.
Once the control has access to the parts (and assuming they exist in the ControlTemplate) we can now attached event handlers to them or manipulate them in different ways.
StyleTypedProperty Attribute
The StyleTypedProperty exposes the properties which are of type Style, for example we wish to extend our ColourPicker to have a property that has the Style applied to the colour selection panel. We declare the property as per any other dependency property (I’ll list below just for completeness)
public static readonly DependencyProperty ColourSelectionPanelStyleProperty = DependencyProperty.Register( "ColourSelectionPanelStyle", typeof(Style), typeof(ColourPicker), new PropertyMetadata(null)); public Style ColourSelectionPanelStyle { get { return (Style)GetValue(ColourSelectionPanelStyleProperty ); } set { SetValue(ColourSelectionPanelStyleProperty , value); } }
So now we have a property of type Style named ColourSelectionPanelStyle. We want to now expose this via the StyleTypedPropertyAttribute as per
[StyleTypedProperty(Property = "ColourSelectionPanelStyle", StyleTargetType = typeof(Panel))] public class ColourPicker : Control { // implementation }
Exposing our style like this means we could easily apply, just the style, to part of our control without the need to clone the whole ControlTemplate.
TemplateVisualState Attribute
In a previous post I talked about using the VisualStateManager (VSM) and how we can create our own strings to represent a control’s different state. These strings are keys defined by the developer to represent various changes within a control.
To publish these states we use the TemplateVisualStateAttribute, for example
[TemplateVisualState(Name = "ColourSelected", GroupName = "SelectionStates")] public class ColourPicker : Control { // implementation }
Now within our ControlTemplate we might have a VSM with the VisualState
<VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="ColourPickerGroup"> <VisualState x:Name="ColourPickedState"> <!-- Some UI specific code --> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
ContentProperty Attribute
The ContentPropertyAttribute can be used to indicate which property is the XAML content property. We might be hard pushed to find a use for the ContentProperty on our example ColourPicker, so instead let’s look at a ResourceKeyConverter class
[ContentProperty("Options")] public class ResourceKeyConverter : MarkupExtension, IValueConverter { public ResourceDictionary Options { get; set; } // implementation }
Now XAML knows that within the ResourceKeyConverter XAML code the content is of type ResourceDictionary, for example
<UI:ResourceKeyConverter x:Key="resourceConverter"> <ResourceDictionary> <SolidColorBrush Color="Red" x:Key="X" /> <SolidColorBrush Color="Green" x:Key="Y" /> </ResourceDictionary> </UI:ResourceKeyConverter>
If the type of the property is not a string or object the XAML procesoor tries to convert the type using native type conversions or look for a type converter.