Derek Lakin

Defining the Default Style for a Lookless Control

Microsoft introduced the concept of “lookless” controls in WPF, which means that the control defines its behaviour without any information about how it actually looks, which is where templates and styles come in. However, all of the default controls provide a default look, which is in keeping with the current Windows theme (Aero in Windows Vista, Luna, Metallic, or Homestead in Windows XP and so on). If you are creating lookless controls in a class library for general consumption, it would be helpful to also provide a default look for your controls. To achieve this, there are three things that you need to do.

Note: This article refers specifically to custom controls (i.e. those that inherit from Control, FrameworkElement or similar, not user controls that inherit from UserControl). For more information about creating controls for WPF, see Control Authoring Overview on the MSDN web site.

1. Override the Metadata for the DefaultStyleKey Property

In a static constructor for your control, set the default style key to the type name, which is used to do style lookups for your control. The following code example shows the static constructor for a Wizard class that I’m creating.

1: static Wizard() 2: { 3: DefaultStyleKeyProperty.OverrideMetadata( 4: typeof(Wizard), 5: new FrameworkPropertyMetadata(typeof(Wizard))); 6: }
[]( ### 2. Define Your Default Style You can create a default style for each of the different Windows themes. To do this you need to create resource dictionary with a specific name in a Themes folder that is a subfolder of the folder that contains your control in your class library. The following table provides the file names of the theme-specific resource dictionaries.
**Resource dictionary****Windows theme**
Classic.xaml“Classic” Windows 9x/2000 look on Windows XP
Luna.NormalColor.xamlDefault blue theme on Windows XP
Luna.Homestead.xamlOlive theme on Windows XP
Luna.Metallic.xamlSilver theme on Windows XP
Royale.NormalColor.xamlDefault theme on Windows XP Media Center Edition
Aero.NormalColor.xamlDefault theme on Windows Vista
You don’t need to provide a style for every theme. If there is no theme-specific resource, then the generic resource is used for the control, which is defined in the Themesgeneric.xaml resource dictionary. The following XAML code example shows the default style in generic.xaml for the Wizard class described above.
1: "" 2: xmlns:local="clr-namespace:DerekLakin.Libraries.Presentation" 3: xmlns:x=""> 4: 5: "Template"> 6: 7: "{x:Type local:Wizard}"> 8: "True"> 9: "Bottom" 10: HorizontalAlignment="Right" 11: Orientation="Horizontal" 12: Margin="10"> 13: 14: Content="Back" /> 15: 16: Content="Next" 17: Margin="4,0,0,0" /> 18: 19: Content="Cancel" 20: Margin="4,0,0,0" /> 21: 22: Content="Finish" 23: Margin="4,0,0,0" /> 24: 25: "PART_Frame" 26: NavigationUIVisibility="Hidden" /> 27: 28: 29: 30: 31: 32:
### 3. Add the ThemeInfo Attribute to AssemblyInfo The final part is to publicise the fact that your assembly contains control-specific resources, which you do by using the [ThemeInfo]( "ThemeInfoAttribute Class") attribute. The [GenericDictionaryLocation]( "ThemeInfoAttribute..::.GenericDictionaryLocation Property ") property defines where the generic resources are and the [ThemeDictionaryLocation]( "ThemeInfoAttribute..::.ThemeDictionaryLocation Property ") property defines where the themed resources are. The value for both properties is a [ResourceDictionaryLocation]( "ResourceDictionaryLocation Enumeration") enumeration, which is [None]( "None Field"), [SourceAssembly]( "SourceAssembly Field"), or [ExternalAssembly]( "ExternalAssembly Field"). The following code example shows the [ThemeInfo]( "ThemeInfoAttribute Class") attribute for the class library project that contains the Wizard class described previously, which has no theme-specific resources, but does specify a generic resource.
1: [assembly: ThemeInfo(ResourceDictionaryLocation.None, 2: ResourceDictionaryLocation.SourceAssembly)]

Side Note

If your custom control has specific parts that are key to the operation of the control (such as the buttons and frame in the Wizard example described previously), then the convention is to name them using the “PART_” prefix in the control template. For each required part in your template, you should add a TemplatePart attribute to the class definition as shown in the following code example.

1: [TemplatePart(Name = "PART_Frame", Type = typeof(Frame))] 2: [TemplatePart(Name = "PART_Back", Type = typeof(Button))] 3: [TemplatePart(Name = "PART_Cancel", Type = typeof(Button))] 4: [TemplatePart(Name = "PART_Finish", Type = typeof(Button))] 5: [TemplatePart(Name = "PART_Next", Type = typeof(Button))] 6: publicclass Wizard : Window
You should get a reference to these named template parts in the **OnApplyTemplate** override of your class because this is the point where the control template has actually been applied. It is common **not** to specifically handle missing template parts so that an exception (typically a NullReferenceException) gets raised at runtime, which clearly indicates that something is broken. The following code example shows the **OnApplyTemplate** override for the Wizard class described previously.
1: publicoverridevoid OnApplyTemplate() 2: { 3: base.OnApplyTemplate(); 4: 5: // Get the template parts. 6: this.navigationFrame = this.GetTemplateChild("PART_Frame") as Frame; 7: this.back = this.GetTemplateChild("PART_Back") as Button; 8: this.cancel = this.GetTemplateChild("PART_Cancel") as Button; 9: this.finish = this.GetTemplateChild("PART_Finish") as Button; 10: = this.GetTemplateChild("PART_Next") as Button; 11: }
For more information about designing lookless controls, see [Guidelines for Designing Stylable Controls]( "Guidelines for Designing Stylable Controls") on the MSDN web site.
This work is licensed under a [Creative Commons Attribution By license.](