This week, I added a WPF custom control to an existing project. I thought it was going to be pretty straight forward, but ran into a couple project configuration issues.
The project that contained my control was initially created with the Visual Studio Class Library template, and did not start life from one of the WPF project templates (this was important, as I found out later). The WPF project templates are WPF Application, WPF Browser Application, WPF User Control Library and WPF Custom Control Library.
A custom control does not have a XAML file, and finds it default style and control template from a generic resource dictionary. The generic resource dictionary is named generic.xaml and is expected to be in project folder named themes.
Inside generic.xaml, there is a ResourceDictionary, and a Style with a TargetType that is the type of the custom control:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ClassLibrary" > <Style TargetType="{x:Type local:CustomControl1}"> : : </Style> </ResourceDictionary>
Additionally, the DefaultStyleKey property should also be set to the type of the custom control:
static CustomControl1() { DefaultStyleKeyProperty.OverrideMetadata( typeof (CustomControl1), new FrameworkPropertyMetadata (typeof (CustomControl1))); }
This seems a strange way to set a dependency property, but is the recommended way to set the DefaultStyleKey:
This property is typically not set through any of its direct property accessors. Instead, you override the type-specific metadata of this dependency property every time you create a new FrameworkElement derived class. When you derive a control, call the OverrideMetadata method against the DefaultStyleKeyProperty identifier, within the static constructor of the control derived class
With everything all setup (or so I thought), I launched the UI harness to verify my work. To my surprise, the control came up blank – the default style (and control template) was not found.
After digging around, and looking at the code generated by the WPF Custom Control Library project template, I stumbled upon the ThemeInfo assembly attribute. The WPF project templates insert a ThemeInfo assembly attribute into the AssemblyInfo.cs file.
[assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific // resource dictionaries are located ResourceDictionaryLocation.SourceAssembly //where the // generic resource dictionary is located )]
The ThemeInfo constructor takes two ResourceDictionaryLocation values. The first value specifies where to look for theme specific resource dictionaries. The second parameter specifies where to look for the generic resource dictionary.
Once I added the ThemeInfo assembly attribute to my project, my custom control appeared as expected.
You can read more about WPF custom controls in WPF in Action chapter 13.