Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Lookless Controls / Themes

, 17 Jun 2009
Rate this:
Please Sign up or sign in to vote.
One of the great things about WPF is that it separates the functionality of a control from the way it looks, this has become known as “lookless controls”. Which is great, but how can we ensure that our custom controls behave and also have a default look in the first place. This mini arti

One of the great things about WPF is that it separates the functionality of a control from the way it looks, this has become known as “lookless controls”. Which is great, but how can we ensure that our custom controls behave and also have a default look in the first place. This mini article will try and show you what happens with lookless controls.

Ensuring Control Does What We Want It To Do

I will firstly talk a bit about how you can create a split designer/developer project that should work correctly.

The first things that a developer should do is create a TemplatePartAttribute such that this is captured in the metadata which can be used by an documentation tool. By using this TemplatePartAttribute the developer is able to tell the designer what was intended for a correct control operation.

Here is an example for a small control that I have made

   1:      [TemplatePart(Name = “PART_DropDown”,
   2:        Type = typeof(ComboBox))]
   3:      public class DemoControl : Control
   4:      {
   5:      }

This should be an alert to the designer that they need to create a part of the control Template that should be a ComboBox and should be called “PART_DropDown”. So that is part1, next the developer should override the OnApplyTemplate and look for any expected parts that are required to make the control work properly and wire up the events required. Here is an example.

   1:          public override void OnApplyTemplate()
   2:          {
   3:              base.OnApplyTemplate();
   4:  
   5:              //Obtain the dropdown and create the items
   6:              dropDown =
   7:                  base.GetTemplateChild(
   8:                  “PART_DropDown”) as ComboBox;
   9:              if (dropDown != null)
  10:                  dropDown.SelectionChanged +=
  11:                      new SelectionChangedEventHandler(
  12:                          dropDown_SelectionChanged);
  13:  
  14:  
  15:          }
  16:  
  17:          void dropDown_SelectionChanged(object sender,
  18:              SelectionChangedEventArgs e)
  19:          {
  20:  
  21:  
  22:          }

Another method is to rely on RoutedCommands that should be used by the designer in the XAML control Template. These can then used as follows:

   1:  
   2:              // Make sure the command is bound, so that it will work when called to
   3:              CommandBindings.Add(new
   4:                  CommandBinding(DemoCommands.SayHello,
   5:                  //The actual command handler code
   6:                  (s, e) => {
   7:                      MessageBox.Show(“Hello”);
   8:                  }));

Lookless Controls

In order to create a truly lookless control, we should do the following:

Override the default Style associated with a control, this is done by changing the metadata. An example of which is as follows:

   1:          static DemoControl()
   2:          {
   3:              //Provide a default set of visuals for a custom control
   4:              DefaultStyleKeyProperty.OverrideMetadata(
   5:                  typeof(DemoControl),
   6:                  new FrameworkPropertyMetadata(
   7:                      typeof(DemoControl)));
   8:          }

Next we need to understand a few things about how Themes work in WPF. There is an assembly level attribute that is called ThemeInfoAttribute, which is typically created as follows:

   1:  [assembly: ThemeInfo(
   2:      ResourceDictionaryLocation.None,
   3:      //where theme specific resource dictionaries are located
   4:      //(used if a resource is not found in the page, 
   5:      // or application resource dictionaries)
   6:      ResourceDictionaryLocation.SourceAssembly
   7:      //where the generic resource dictionary is located
   8:      //(used if a resource is not found in the page, 
   9:      // app, or any theme specific resource dictionaries)
  10:  )]

This could be used to indicate a location for a Style for a control. More often than not this is created as I have just shown. If you do not specify an external Dll to look in, the next place that is examined is Themes\generic.xaml, so this is where you should put your default Style/Template for your custom control.

So typically you would create a generic.xaml file that held the default control Style/Template.

For the attached demo project my generic.xaml simply contains a bunch of merged resource dictionary objects as follows:

   1:  <ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
   2:      xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”>
   3:  
   4:      <!– Merge in all the available themes –>
   5:      <ResourceDictionary.MergedDictionaries>
   6:          <ResourceDictionary
   7:              Source=”/CustomControls;component/Themes/Default.xaml” />
   8:          <ResourceDictionary
   9:              Source=”/CustomControls;component/Themes/Blue.xaml” />
  10:          <ResourceDictionary
  11:              Source=”/CustomControls;component/Themes/Red.xaml” />
  12:      </ResourceDictionary.MergedDictionaries>
  13:  
  14:  
  15:  
  16:  </ResourceDictionary>

If we study one of these, a little more closely, say the “Blue” one, we can see that is also uses a ComponentResourceKey markup extension.

   1:  <ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
   2:      xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
   3:      xmlns:local=”clr-namespace:CustomControls”>
   4:  
   5:  <Style x:Key=”{ComponentResourceKey {x:Type local:DemoControl}, Blue }”
   6:         TargetType=”{x:Type local:DemoControl}”>
   7:      <Setter Property=”Background” Value=”Blue”/>
   8:      <Setter Property=”Margin” Value=”10″/>
   9:      <Setter Property=”Template”>
  10:          <Setter.Value>
  11:              <ControlTemplate TargetType=”{x:Type local:DemoControl}” >
  12:                  <Border Background=”{TemplateBinding Background}”
  13:                          CornerRadius=”5″ BorderBrush=”Cyan”
  14:                          BorderThickness=”2″>
  15:                      <StackPanel Orientation=”Vertical”
  16:                                  Margin=”{TemplateBinding Margin}”>
  17:                          <Button x:Name=”btnSayHello”
  18:                                  Margin=”{TemplateBinding Margin}”
  19:                                  Background=”LightBlue”
  20:                                  Foreground=”Black”
  21:                                  Command=”{x:Static 
  22:                                  local:DemoCommands.SayHello}”
  23:                                  Content=”Say Hello” Height=”Auto”
  24:                                  Width=”Auto” />
  25:                          <ComboBox x:Name=”PART_DropDown”
  26:                                    Margin=”{TemplateBinding Margin}”
  27:                                    Background=”LightBlue”
  28:                                    Foreground=”Black”>
  29:                              <ComboBoxItem Content=”Blue”/>
  30:                              <ComboBoxItem Content=”Red”/>
  31:                          </ComboBox>
  32:                      </StackPanel>
  33:                      <Border.LayoutTransform>
  34:                          <ScaleTransform CenterX=”0.5″
  35:                                          CenterY=”0.5″
  36:                                          ScaleX=”3.0″
  37:                                          ScaleY=”3.0″/>
  38:                      </Border.LayoutTransform>
  39:                  </Border>
  40:              </ControlTemplate>
  41:          </Setter.Value>
  42:      </Setter>
  43:  </Style>
  44:  
  45:  
  46:  </ResourceDictionary>

So lets get to the bottom of that. What does that do for us. Well quite simply it allows us another way to select a resource, by using a Type/Id to lookup the resource. Here is an example

   1:              Style style = (Style)TryFindResource(
   2:                  new ComponentResourceKey(
   3:                      typeof(DemoControl),
   4:                      styleToUseName));
   5:  
   6:              if (style != null)
   7:                  this.Style = style;

The working app simply allows users to toggle between 3 different Styles for the lookless control. You can download it and play with using the demo project, theming.zip - 92.64 KB

Enjoy.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
QuestionStatic key? PinmemberSilentMember3-May-12 21:42 
AnswerRe: Static key? PinmvpSacha Barber3-May-12 21:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 17 Jun 2009
Article Copyright 2009 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid