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>
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:
So the Implicit Style option looks pretty good. You just have to always remember adding one line of code when you’re changing styles
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
- A new class appeared
- 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.
- If you open Properties\AssemblyInfo.cs you’ll notice this line was added: [assembly: ThemeInfo(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:
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:
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:
- Check out WPF Merged Dictionary problems and solutions to see how you can use Merged Dictionaries to spread your resources across many XAML files (and all the problem WPF has in store for us).
- Check out WPF complete guide to Themes and Skins to see how to add Themes to your WPF app.
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?
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 :)
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.
Hi Daniel,
With Google Translate I figured you want to "switch" between styles by chossing them from a combo box. You absolutely can do that. Check out my WPF complete guide to Themes and Skins article.
I really like this site
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.microso.... But the behavior doesn't seem limited to windows.
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}}"
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..
Those code samples again.
Default:
"
Implicit:
"
I can't see the code part in your comment. Please email me at michaels9876@gmail.com
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.