Andrej Tozon's blog

In the Attic


Do it with Style!

In my previous post I've shown how to create a named brush resource in WPF, which can be used and reused through whole application. Although I gave a brush a meaningful name (WindowBackgroundBrush), it can as well be used to paint areas other than window backgrounds. Naming brushes this way is more of a visual hint to me: whenever I feel the need to change the looks of windows' background, the WindowBackgroundBrush would most likely catch my eye first. If you ever looked at the SystemColors class in Windows Forms (that's in System.Drawing namespace), this convention will feel familiar; the difference is that here we're dealing with brushes, while with SystemColors it's plain colors that we were using. To make the transition easier, the equally named class is also available in WPF (it's in System.Windows namespace). While still referencing all system colors, this new class incidentally includes additional properties to make those system colors available also as solid color brushes.

To conclude - creating custom brushes and naming it by their purpose is a lot like building a closed set of named colors you'll be using in your application. If you already have an idea of how your application should (visually) look like, or your designer has already come up with a theme or a style book, this kind of convention can constrain you to pre-chosen color set and and the same time keep you away from experimenting with some weird color combinations.

I really can't remember where I originally got this from, but whenever a hear a word style in the context of software development, application skinning is the first thing that comes to my mind. Skinning is basically a process of creating a visual theme for your application, where User Interface is built in a way that can be quickly changed simply by applying a new "skin" to it. And while WPF styles can by all means help you built skinable application, they are so much more than that.

To put it simple - WPF styles are sets of properties, which can be applied to a named element or a specific element type. A style can specify any property that a target object allows setting, no matter if it affect its visual rendering. For example, I might have a style which sets textbox's background and text color, and at the same time limits the length of inputted text by setting its MaximumLength property:

<Style x:Key="MyTextBoxStyle" TargetType="{x:Type TextBox}">
  <Setter Property="Foreground" Value="Yellow" />
  <Setter Property="Background" Value="Black" />
  <Setter Property="MaxLength" Value="10" />

Knowing what a simple style can do, we can return to the code snippet from the previous post. Remember - the goal was to specify how window's background should be painted without setting the Background property directly. Here's current page's full XAML:

    <LinearGradientBrush x:Key="WindowBackgroundBrush" StartPoint="0,0" EndPoint="0,1">
      <GradientStop Color="#FF333333" Offset="0.0" />
      <GradientStop Color="#FF595959" Offset="1.0" />
    <Style TargetType="{x:Type Page}">
      <Setter Property="Background" Value="{StaticResource WindowBackgroundBrush}" />

Notice that I'm no longer setting Page's Background property. This is now done through style (marked bold part above) - by not specifying the key of the style, this style will be applied to Page elements within the style's scope. In this example, the scope is the current page, but if we were to move the style to the application level resources, it would be applied to all Pages within application, which don't explicitly specify some other style. We'll show how this works when this sample "application" gets a bit more complex.

OK, we now defined a brush and a style, which uses that brush to paint the window's background. We're now able to control how window gets painted without even touching the code for it. All we have to do is modify a style. And if we delete the style completely, guess what... the window will get painted with the default system color.

And this is how application looks so far. It's a loose Xaml page so you need .NET FX 3.0 to view it.