Skip Navigation LinksHome > View Post

Reason 5. Styles

Earlier I introduced a series of posts entitled 10 reasons to consider WPF for your next desktop application. If you haven't read the intro yet, better head on back and read it first.

To demonstrate the power of styles in WPF we're going to take the code from the example in Reason 4 and we're going to add one file. And that's it. We're not going to change any of the existing files. No changes to the Xaml behind the window or the c# code in the project. Clear?

The file we're going to add is a Resource Dictionary. I've talked about Resource Dictionaries in Xaml before - don't worry they're easy.

Dammit. Time to come clean, there'll be one tiny change to the existing Xaml in the App.Xaml file, but just one! And this is it:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="styles.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

We needed to add a reference to our new resource dictionary, called 'styles.xaml'. No more changes to existing files, promise.

Because we added this at the application level this dictionary is available anywhere in the application. Let's get started with our styles then.

The need for Styles in WPF has clearly been inspired by CSS from the world of HTML. Yet, whilst what can be achieved is very similar the implementation is, in my opinion, quite different.

Styles make use of the Setter that we first met in Reason 4: Triggers. Here's an example style:

<Style x:Key="BigText">
    <Setter Property="Control.FontSize" Value="50">
</Style>

When applied to an element (that inherits from Control) this style would set the FontSize to 50. Easy eh? And this is how we would apply it to a TextBox

<TextBox Style="{StaticResource BigText}">Hello</TextBox>

Big Hello

Alternatively, we could specify the TargetType on the Style itself like so...

<Style TargetType="{x:Type TextBox}">
    <Setter Property="Control.FontSize" Value="50">
</Style>

Now this style will automatically apply to all textboxes (within the scope of where the style was defined) and we don't need to specify the style on each TextBox. Neat. Let's put our new found knowledge into practice with this post's example.

We'll start with some brushes. Pretty gradients in fact.

<LinearGradientBrush x:Key="_brshHorizon" EndPoint="0.5,0.065" StartPoint="0.5,0.979">
    <GradientStop Color="#FF000000" Offset="0"/>
    <GradientStop Color="#FF363636" Offset="1"/>
    <GradientStop Color="#FF1B1B1B" Offset="0.5"/>
    <GradientStop Color="#FF5B5B5B" Offset="0.543"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="_brshBorders" EndPoint="0.081,0.011" StartPoint="0.919,0.989">
    <GradientStop Color="#FF000000" Offset="0"/>
    <GradientStop Color="#FF949494" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="_brshText" EndPoint="0,0" StartPoint="0,1">
    <GradientStop Color="#FFFFFFFF" Offset="1"/>
    <GradientStop Color="#FF8D8D8D" Offset="0"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="_brshMirror" EndPoint="0.081,0.011" StartPoint="0.919,0.989">
    <GradientStop Color="#FFD0D0D0" Offset="1"/>
    <GradientStop Color="#FF757575" Offset="0"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="_brshTextDark" EndPoint="0,0" StartPoint="0,1">
    <GradientStop Color="#FF000000" Offset="0"/>
    <GradientStop Color="#FF646464" Offset="1"/>
</LinearGradientBrush>

And now for some styles:

<Style TargetType="{x:Type src:Window1}">
    <Setter Property="FontSize" Value="16" />
    <Setter Property="Background" Value="{StaticResource _brshHorizon}"/>
</Style>
<Style TargetType="{x:Type Label}">
    <Setter Property="Foreground" Value="{StaticResource _brshText}" />
</Style>
<Style TargetType="{x:Type Control}" x:Key="_styleA">
    <Setter Property="Background" Value="{StaticResource _brshMirror}"/>
    <Setter Property="Foreground" Value="{StaticResource _brshTextDark}"/>
    <Setter Property="BorderBrush" Value="{StaticResource _brshBorders}"/>
    <Setter Property="FontWeight" Value="Bold" />
    <Setter Property="BorderThickness" Value="2"/>
</Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource _styleA}" />
<Style TargetType="{x:Type GroupBox}" >
    <Setter Property="Foreground" Value="White" />
</Style>
<Style TargetType="{x:Type ListBox}" BasedOn="{StaticResource _styleA}" />
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource _styleA}">
    <Setter Property="Foreground" Value="{StaticResource _brshText}" />
    <Setter Property="Background" Value="{StaticResource _brshHorizon}" />
</Style>

Notice how we've used the BasedOn attribute of the Style element to inherit from other styles - a nice way of getting some reuse.

And the result?

With added 'style'

The great thing about styles is you can do much more than styling. A Setter can set pretty much any property on an element, including events! So Styles can be used to change behaviour - maybe you'd have a draggable style?

And because they can set any property Styles can even be used to fundamentally change the appearance of a control by applying a new ControlTemplate (as discussed in this post).

Why not have a play with the Reason 5 ClickOnce Sample.

BIG FAT DISCLAIMER NOTICE!

A lof of what I've demonstrated in this post isn't something I would normally encourage. I'm actually a big fan of the consistency afforded by windows and I've written before about User Interfaces that waste time trying to look 'pretty' when they should stick to the basics. When I first heard about WPF (or Avalon) I had grave concerns that all my desktop applications would have spinning 3D cubes with a different video playing on each surface. Thankfully, this hasn't happened to date and I'm hoping we've all learned our lesson from the horrors of early web applications.

Furthermore, there's another reason not to go with a custom UI like this. When the next version of Windows releases (and it won't be a six year wait this time :) your application will suddenly look totally out of date. Believe it or not this example took a long time, and it still looks pants. And I have a (distant) background in design. Just say no.

Nonetheless, I still find Styles very useful.

Styles FAQ Numero Uno.

To pre-empt the inevitable comments I'm going to tackle the #1 FAQ on styles right here, right now.

Q: Why isn't my style working?

<Style x:Key="BigText">
    <Setter Property="Control.FontSize" Value="50" />
</Style>

<TextBox Style="{StaticResource BigText}" FontSize="10">Hello</TextBox>

A: Because locally-set values take precedent over those set in styles. So in this case, the local FontSize="10" wins.

Tags: WPF

 
Josh Post By Josh Twist
2:22 AM
27 Oct 2007

» Next Post: Binding to the Current Item in WPF
« Previous Post: LINQPad and the LINQPad challenge

Comments are closed for this post.

Posted by Matt @ 12 Mar 2009 5:06 AM
Thanks, Josh - awesome series. But I'm not familiar with the expression "looks pants" - is that a good or a bad thing?

Posted by josh @ 07 Apr 2009 7:01 AM
pants == rubbish. A bad thing :)

Posted by Alex @ 19 Jul 2009 9:56 PM
Between the use of the term "pants" and the picture of Trigger from Only Fools on the previous entry, I think it's fair to say that Josh is a Great Brit :-)

© 2005 - 2014 Josh Twist - All Rights Reserved.