Andrej Tozon's blog

In the Attic

NAVIGATION - SEARCH

Label.TargetProperty as a Source

One of probably most underused features in windows desktop UI development world is got to be the use of a Label as an accessor to its pair control. I rarely see developers using this possibility to enhance keyboard UI navigation.

A couple of things changed about this mechanism when going from Windows Forms to WPF:

  1. The escape character to mark the mnemonic (the letter/key for accessing related control) changed from ‘&’ to ‘_’;
  2. In Windows Forms, the control that received focus was always the one that was next in tab order. With WPF, the pair control is specified with the Label.Target dependency property.

A simple form with this feature enabled would look like:

   1: <Grid>
   2:   ...
   3:   <Label Target="{Binding ElementName=firstNameBox}" Content="_First name:"/>
   4:   <TextBox Name="firstNameBox" Grid.Column="1"/> 
   5:   <Label Target="{Binding ElementName=lastNameBox}" Content="_Last name:" Grid.Row="1" />
   6:   <TextBox Name="lastNameBox" Grid.Column="1" Grid.Row="1"/> 
   7:   ...
   8: </Grid>

The best thing about Label's Target property is not only it sets focus to its pair control, but can also pose as a bridge back to the Label when we need to provide some feedback from the target control. For example, when a TextBox gets disabled,  we would want the associated Label render disabled as well. This is fairly easy to achieve through binding. All we need is a simple style for a label:

   1: <Style TargetType="{x:Type Label}">
   2:   <Setter Property="IsEnabled" Value="{Binding Path=Target.IsEnabled, RelativeSource={RelativeSource Self}}" />
   3: </Style>

The key is in binding's path, which queries target's IsEnabled property and sets it accordingly.

This opens up a range of possibilities. One of my favorite UI tricks is to visually differentiate the focused control from the others by slightly contrasting the label, associated with the focused control.  Label.Target is perfect for this.

I built on my previous example and the result now looks like this:

And here's the Label style I used:

   1: <Style TargetType="Label">
   2:   <Setter Property="Foreground" Value="{StaticResource LabelTextBrush}" />
   3:   <Style.Triggers>
   4:       <DataTrigger Binding="{Binding Path=Target.IsFocused, RelativeSource={RelativeSource Self}}" Value="True" >
   5:           <Setter Property="Control.Foreground" Value="{StaticResource FocusedLabelTextBrush}" />
   6:       </DataTrigger>
   7:   </Style.Triggers>
   8: </Style>

It's similar to the previous one, it just uses a DataTrigger to set Label's foreground color instead of direct property mapping.

I also added a short animation to make TextBoxes flash for a moment when they receive focus.

You can see all this in action by downloading and executing this Loose Xaml page.

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" />
</Style>

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:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Page.Resources>
    <LinearGradientBrush x:Key="WindowBackgroundBrush" StartPoint="0,0" EndPoint="0,1">
      <GradientStop Color="#FF333333" Offset="0.0" />
      <GradientStop Color="#FF595959" Offset="1.0" />
    </LinearGradientBrush>
    <Style TargetType="{x:Type Page}">
      <Setter Property="Background" Value="{StaticResource WindowBackgroundBrush}" />
    </Style>
  </Page.Resources>
</Page>

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.

In the beginning, it was Black

This is the first post of what I would like to believe will be a series of thoughts on creating a good User Experience with Windows Presentation Foundation. I have no idea where this will take me, but I'll start with a color.

You like black? We've got black.

Black

How about using it as a background color in a WPF application?

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Background="Black">
</Page>

That's a nice black page, isn't it? But wait a minute! Is this way of hardcoding the color into the Page really a good idea? What options do we have here?

First, it's important to know that when setting Background="Black" in XAML, it's a solid black brush that you're actually setting to the Background property. This is possible with the help of a type converter called BrushConverter, which is responsible for creating a brush from as many string representations of a color as possible.

Now we're talking brushes... There are six kinds of brushes in WPF and we'll eventually get to know and use all of them. For now, it's the SolidColorBrush we're interested in [no need explaining what this one looks like I presume].

So, instead of painting our background with a solid black color, we can just paint it with "a brush" and define what "a brush" looks like elsewhere:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Background="{DynamicResource WindowBackgroundBrush}">
  <Page.Resources>
      <SolidColorBrush x:Key="WindowBackgroundBrush" Color="Black" />
  </Page.Resources>
</Page>

In this example, the solid black brush is defined within Page's resources and you probably noticed that it's got a new name now. We're not calling it by its color anymore; from now on we'll be addressing it by its purpose - to paint a window's background. This kind of abstracting away from the actual color is important because a week or two from now you might want to rethink your previous color choice and change it to some other color. You don't believe me? Just ask your wife...

In fact, pitch black color is not the perfect choice for an application window's back color anyway. We'll probably make the user's eyes more happy by putting some light in it, for example:

<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FF595959" />

Note that the color is now specified with the "webby" hexadecimal code.

If you're an adventurous type and like fancy design, you may even consider using a linear gradient brush, which will blend two or more colors across a line, pointed at whichever direction you like. You can even specify gradient stop points to customize the color transition lengths. The following construct uses two shades of dark gray to create a linear vertical gradient:

<LinearGradientBrush x:Key="WindowBackgroundBrush" StartPoint="0,0" EndPoint="0,1">
  <GradientStop Color="#FF333333" Offset="0.0" />
  <GradientStop Color="#FF595959" Offset="1.0" />
</LinearGradientBrush>

Notice that the name of the brush remains the same as before, therefore we don't need to touch the rest of the code. The page still gets painted just by "a brush". While that might be the end of the story for some, the others would ask themselves - "OK, but what if I don't want to paint the background at all?"

And that's where WPF Styles kick in... I'll address that in my next post.