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:

Share:

Enjoy the blog? I would love you to subscribe! Performance Optimizations in C#: 10 Best Practices (exclusive article)

11 thoughts on “Explicit, Implicit and Default styles in WPF”

  1. 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?

    1. 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 🙂

  2. Hola michael,

    me gustaría saber si tu sabes como puedo utilizar diferentes tipos de estilo y poderlos seleccionar desde un combobox nose si entiendes lo que quiero hacer.

    digamos que ya corriendo la aplicación, poder elegir de los estilos que tenga en la carpeta themes y aplicarlos en ese mismo instante ya sea mediante un combobox o cualquier otro elemento.

  3. Tanner Stevenson

    Hi Michael,

    Thanks for the blog. I was wondering if you may be able to help me with a problem I am having related to default and implicit styles?

    I have two custom controls that represent a custom base window and a base page (to provide consistent styling among all windows and pages). I have added default styles with default templates to generic.xaml and the default styles get applied correctly. In order to make the controls customizeable, I used template bindings to existing properties like Background and to a few custom dependency properties. To make sure the template bindings work, I made implicit styles and added them to the merged dictionaries in my app.xaml file, however the values in the implicit styles are never applied to the controls defined in the templates. The confusing thing is if I look at the live visual tree, I can see the implicit style being applied to the page or the window, however, the children controls defined by the default template are not updating their template-bound values based on the implicit style. The only way I have been able to get around this is to put the following code in the constructor:

    var style = this.TryFindResource(typeof(BasePageView)) as Style;

    if (style != null)
    this.Style = style;

    However this seems very hacky and completely unnecessary based on how my understanding of implicit styling and template bindings. Do you have any idea what I might be missing?

    I first got the idea for the above from https://social.msdn.microsoft.com/Forums/vstudio/en-US/7c119e29-e7e3-4e9a-b732-7ee0e050c0dd/why-a-window-style-couldnt-apply-to-wpf-windows-automatically-without-xkey-property?forum=wpf. But the behavior doesn’t seem limited to windows.

    1. Hi Tanner,
      Let’s see if I understand correctly. You define a custom control MyWindow. That Window has a child control Button.
      You want the Button’s background to change according to the Window’s Background and it doesn’t work. Is this the problem?
      The reason is that when you do this:

      Button Background=”{TemplateBinding Background}”

      The Background is taken from the Button’s Background, not from the Window. The reason is that some properties are inherited from the parent (like DataContext) and some aren’t (like Background).
      To have the Button’s background be taken from the Window / Page, you can do this:

      Button Background=”{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type Window}}}”

      Alternatively, you can specify the property by element name

      Button Background=”{Binding Background, ElementName=Mywindow}”

      Also, if I misunderstood the problem, the solution might be a problem with TemplateBinding. TemplateBinding doesn’t work correctly in some scenarios.
      Instead, try using it like this:
      Button Background=”{Binding Background, RelativeSource={RelativeSource TemplatedParent}}”

      1. Tanner Stevenson

        You mostly have it correct. I have a custom, lookless control for which I have defined a default style with a control template. For example:

        Now if I define another style in a resource dictionary that is added to App.xaml like so:

        The new values for padding and background will be applied to the BasePageView, however the Border and ContentPresenter will not receive those values and will therefore display the values defined in the default style.

        I have tried changing the binding in every way possible, even by explicitly declaring the AncestorType, but none of them work..

  4. Internet advertising is an revenue-producing alternative dependent on the utilization of interactive know-how (through the Internet) to construct a dialog with potential purchasers by connecting with them by way of high quality content material about your product and/or service offerings.

Comments are closed.