Explicit, Implicit and Default styles in WPF

Let’s say we want to change the looks of all buttons in our application.
We want them to have a black background and text inside to have white foreground.

WPF gives us a Style infrastructure to do just this. But, as often happens with WPF, we can achieve this in many different and confusing ways.
Let’s make some order out of the chaos.

Solution #1: Explicit styles

Explicit style means you’ll have to explicitly write the style for each button. This is best done with a WPF Resource like this:
<Button Style=”{StaticResource BaseButtonStyle}>Hello world</Button>

And in App.xaml you’ll have the resource itself:
<Application.Resources>
<Style x:Key=“BaseButtonStyle” TargetType=“Button”>
<Setter Property=“Background” Value=“Black”/>
<Setter Property=“Foreground” Value=“White”/>
</Style>
</Application.Resources>

NOTE: We can write both TargetType=”Button” and TargetType=”{x:Type Button}”. Both will work for built-in WPF controls.

And the result is:

Resources defined in App.xaml are available everywhere in the application.
We can write the style once and simply add Style=”{StaticResource BaseButtonStyle}” everywhere.

But we wanted to do this to all buttons, right? Isn’t there something that does this to all buttons automatically?

Worry not, WPF has Implicit styles just for this.

Solution #2: Implicit styles

The Microsoft folks did think of a way to apply a resource automatically. You simply need to omit the x:Key in the style and it will be applied to all Controls of type in TargetType.

In App.xaml:
<Application.Resources>
<Style TargetType=“Button”>
<Setter Property=“Background” Value=“Black”/>
<Setter Property=“Foreground” Value=“White”/>
</Style>
</Application.Resources>

In your Window / Control:
<Button>Hello world</Button>

And the result is:

This is called Implicit Style.
Looks great, right? So I’m all set?

Well… not quite yet. Let’s say you want to add somewhere Padding as well in style.
If you write something like this
<Button>
<Button.Style>
<Style TargetType=“Button”>
<Setter Property=“Padding” Value=“20”/>
</Style>
</Button.Style>
Hello world
</Button>

The result will be:

Wait… I see the padding, but where did my Background and Foreground go??

Well, you’ve actually overridden your style.
If you did want to use the original style as well, you could write something like this:

<Button>
<Button.Style>
<Style TargetType=“Button” BasedOn=”{StaticResource {x:Type Button}}>
<Setter Property=“Padding” Value=“20”/>
</Style>
</Button.Style>
Hello world
</Button>

Note that the name of the Resource is actually the type of Button {x:Type Button}
This is because when you create and implicit style with no key, WPF actually gives it a key. And that key is the TargetType you wrote.

So the Implicit Style option looks pretty good. You just have to always remember adding one line of code when you’re changing styles

BasedOn=”{StaticResource {x:Type Button}}

This is one of those things that are bound to be forgotten and leave a person frustrated half a day, wondering how his button changes when he adds a Style for a trigger he must have.
That’s why you might see duplication of styles and other terrible XAML crimes.

Consider this: WPF does give default style to a button. It includes a default template, a default Background, default padding and so on. And when you add a Style, these aren’t overridden.
You can create a Default style yourself as well. But it has to be your own Custom Control. You will not be able to change WPF’s built-in controls` default style.

 

Solution 3: Default styles

As I said, your own Default Styles can be applied only to Custom Controls. And actually, WPF takes care of this pretty much automatically when you add a new item.

Let’s add a Custom Control

Select a WPF Custom Control

Let’s see what changes happened in the solution

  1. A new class appeared
public class MyCustomButton : Control
{
static MyCustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomButton), new FrameworkPro  pertyMetadata(typeof(MyCustomButton)));
}
}

This line in the static constructor is essential. If commented, your control will receive the default style of Control. Or whatever FrameworkElement you derive from.

  1. Themes\Generic.xaml was added to your project
    In there you’ll see style was added for your control.

Generic.xaml is a special file that contains your default styles. You can’t rename it or move it to a different directory.
It is also loaded before your app.xaml resources. So you can’t rely on them if using StaticResource. (Use DynamicResource).
In the Themes directory you can add themes for specific Windows theme. For example adding Aero.NormalColor.xaml means when Windows is in Aero style, these resource file will be loaded.

  1. If you open Properties\AssemblyInfo.cs you’ll notice this line was added:
    [assemblyThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly )]

This line is also essential. It tells the assembly to load Themes\Generic.xaml and use it for default styles.

Let’s try using our default style and see what happens.

First, I wanted a button, so let’s derive from Button:

public class MyCustomButton : Button
{
static MyCustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomButton), new FrameworkPro  pertyMetadata(typeof(MyCustomButton)));
}
}

 

Now let’s go to Generic.xaml and modify our style a bit

<Style TargetType=”{x:Type local:MyCustomButton}>
<Setter Property=“Foreground” Value=“White”/>
<Setter Property=“Background” Value=“Black”/>
<Setter Property=“Padding” Value=“2”/>
<Setter Property=“Template”>
<Setter.Value>
<ControlTemplate TargetType=”{x:Type local:MyButton}>
<Border Background=”{TemplateBinding Background}
   BorderBrush=”{TemplateBinding BorderBrush}
     BorderThickness=”{TemplateBinding BorderThickness}
     Padding=”{TemplateBinding Padding}
      >
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Let’s see how it looks in our window
<local:MyCustomButton>My custom Button</local:MyCustomButton>

This code will show this

Great so far but what if I set the style to something else? Will the default style be overridden? Let’s try:

<local:MyCustomButton>
<local:MyCustomButton.Style>
<Style TargetType=”{x:Type local:MyCustomButton}>
<Setter Property=“Padding” Value=“30”/>
</Style>
</local:MyCustomButton.Style>
My custom Button
</local:MyCustomButton>

And the result is:

Finally… Perfection!

 

Summary:

  • In your application you’ll probably have a project just for your custom controls. Make sure to use Default styles for those controls, not Implicit or Explicit styles. It will make your life easier.
  • Instead of using WPF’s built-in controls, prefer using Custom Controls. This will allow you using Default styles and you’ll probably want to set your own Template anyway.
    WPF’s default template adds a lot of noise you’ll want to remove or customize anyway. For example triggers for Focus, MouseOver and so on that change the appearance.

Generic.xaml is like magic. Make sure your team knows how it works or they will get stuck and end up creating ugly hacks that will cause bugs and annoy everyone.

 

More articles:

4 thoughts on “Explicit, Implicit and Default styles in WPF

  1. Pingback: WPF Merged Dictionary problems and solutions | Michael's Coding Spot

  2. Pingback: WPF XAML Resource Benchmark - Michael's Coding Spot

  3. Bill

    Hi Michael,

    Just discovered your blog, been enjoying reading your articles, thanks for writing them. 🙂

    At our company we essentially use option 2 (Implicit Styles) partly for “developer ergonomics”. It’s hard to tell our consuming teams to not use the default items and much easier to just enforce the styling. Although occasionally they break things if they modify the styles to adjust a trigger, etc.

    Wondering with your option 3 how do you deter your consumers from using the default controls?

    Reply
    1. michaels9876@gmail.com Post author

      Hi Bill, great to hear you’re enjoying my blog.

      Once you’ve created a bunch of custom control you want used, you can arrange some sort of meeting with the consuming teams, presenting all the advantages of the custom controls.

      A bit more aggressive approach is to create an implicit style for the default control, that overrides the Template to show a big red TextBlock saying “This Control is deprecated. Use ‘MyControl’ instead”.
      There’s a change that might not be very well received, so please tell what happens if you go this way 🙂

      Reply

Leave a Reply