1
2
3
4
5
6

Recreating Simple Windows Forms in WPF and XAML (Part 4)

Last time we covered breaking our theming out into an external file, and how to style all instances of a given UI element. We left off needing to know the details of how to style more complicated items like buttons and their mouse-over and mouse-down appearances - that is what we will cover in this final part of the series.

The biggest thing we need to keep in mind with items like buttons or comboboxes is that their appearance can drastically differ based on a computer's operating system. You can get a feel of this by changing the Foreground of a button in Vista and seeing how when a button has focus its fill actually pulsates from the normal appearance to the mouseover appearance and back - this animation is unique to Vista and not seen in XP.

(click 'read more' for the rest of the article..)

Here are a few screenshots showing the difference in our simple form between XP and Vista:

XP

Vista

A few differences to note between how Vista and XP are rendering the form differently:

  • Buttons are styled differently - font and foreground fill
  • Groupbox in Vista has white edging which is hiding the fading gradient brush
  • Textboxes are styled differently - edging and rounded corners
  • Combobox and checkboxes are styled differently
  • Header textblocks are using different default fonts

Similar differences in appearance based on OS and theme occur with pretty much all the standard UI elements, and I can only assume that is why "Simple Styles" were created and distributed with Expression Blend and even Kaxaml. These serve as simple templates to use when creating your own custom appearance for controls without having to worry about what OS and theme the user is using. If we were doing a standard non-stylized windows form in XAML we wouldn't really have to worry about these theme based differences, but given that we are styling the forms it is important that we either review our forms on each OS, or make use of our own templates based off of the Simple Styles provided for WPF.

I decided to try and recreate the Vista look of a button based off of the Button simple style provided in Kaxaml. It looks pretty much the same, but doesn't have the animation between mouse-over and normal appearances like the default button would in Vista. This template might be of use to someone else who wants to base a button theme off of the Vista look, and it shows pretty clearly how each state of the button is themed - you can also see how Resources like LinearGradientBrushes can be defined within the style definition itself:

<Style TargetType="{x:Type Button}" x:Key="simpleButtonStyle">
<Style.Resources>
  <LinearGradientBrush x:Key="simpleButtonInnerBorder" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFFFFFFF" Offset="0" />
    <GradientStop Color="#FFE8E8E8" Offset="1" />
  </LinearGradientBrush>      
  <SolidColorBrush x:Key="simpleButtonOuterBorder" Color="#FF707070" />
  <SolidColorBrush x:Key="simpleButtonDisabledFill" Color="#FFF4F4F4" />
  <LinearGradientBrush x:Key="simpleButtonFill" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFF1F1F1" Offset="0" />
    <GradientStop Color="#FFE8E8E8" Offset="0.5" />
    <GradientStop Color="#FFD2D2D2" Offset="0.5" />
    <GradientStop Color="#FFD2D2D2" Offset="1" />
  </LinearGradientBrush>      
  <LinearGradientBrush x:Key="simpleHighlightBorder" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FF05CDFF" Offset="0" />
    <GradientStop Color="#FF04CCFF" Offset="1" />
  </LinearGradientBrush>            
  <LinearGradientBrush x:Key="simpleHighlightFill" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFE7F5FD" Offset="0" />
    <GradientStop Color="#FFD4EEFC" Offset="0.5" />
    <GradientStop Color="#FFBDE6FD" Offset="0.5" />
    <GradientStop Color="#FFA9DAF6" Offset="1" />
  </LinearGradientBrush>                  
  <LinearGradientBrush x:Key="simplePressedFill" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFC2E4F6" Offset="0" />
    <GradientStop Color="#FFBDE2F5" Offset="0.5" />
    <GradientStop Color="#FFA9DAF3" Offset="0.5" />
    <GradientStop Color="#FF91CCEB" Offset="1" />
  </LinearGradientBrush>                        
  <SolidColorBrush x:Key="simplePressedBorder" Color="#FF2C628B" />
  <SolidColorBrush x:Key="simplePressedBorder2" Color="#FF95ACB9" />
</Style.Resources>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
        <Border 
          x:Name="ButtonOuterBorder"  
          CornerRadius="3" 
          BorderThickness="1"             
          BorderBrush="{StaticResource simpleButtonOuterBorder}">
          <Border 
            x:Name="ButtonPrimaryBorder"  
            CornerRadius="1" 
            BorderThickness="1"
            Background="{StaticResource simpleButtonFill}"
            BorderBrush="{StaticResource simpleButtonInnerBorder}">
            <ContentPresenter 
              Margin="2"
              HorizontalAlignment="Center"
              VerticalAlignment="Center"
              RecognizesAccessKey="True"/>
          </Border>
        </Border>
        <ControlTemplate.Triggers>
          <Trigger Property="IsKeyboardFocused" Value="true">
            <Setter TargetName="ButtonOuterBorder" Property="BorderBrush" 
                    Value="{StaticResource simplePressedBorder}" />
            <Setter TargetName="ButtonPrimaryBorder" Property="BorderBrush" 
                    Value="{StaticResource simpleHighlightBorder}" />
          </Trigger>
          <Trigger Property="IsDefaulted" Value="true">
            <Setter TargetName="ButtonOuterBorder" Property="BorderBrush" 
                    Value="{StaticResource simpleHighlightBorder}" />
          </Trigger>
          <Trigger Property="IsMouseOver" Value="true">
            <Setter TargetName="ButtonPrimaryBorder" Property="Background" 
                    Value="{StaticResource simpleHighlightFill}" />
          </Trigger>
          <Trigger Property="IsPressed" Value="true">
            <Setter TargetName="ButtonOuterBorder" Property="BorderBrush" 
                    Value="{StaticResource simplePressedBorder}" />
            <Setter TargetName="ButtonPrimaryBorder" Property="BorderBrush" 
                    Value="{StaticResource simplePressedBorder2}" />
            <Setter TargetName="ButtonPrimaryBorder" Property="Background" 
                    Value="{StaticResource simplePressedFill}" />
          </Trigger>
          <Trigger Property="IsEnabled" Value="false">              
            <Setter TargetName="ButtonPrimaryBorder" Property="Background" 
                    Value="{StaticResource simpleButtonDisabledFill}" />
            <Setter TargetName="ButtonOuterBorder" Property="Background" Value="#EEEEEE" />
            <Setter TargetName="ButtonOuterBorder" Property="BorderBrush" Value="#AAAAAA" />
            <Setter Property="Foreground" Value="#ADADAD"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

It's worth noting that there are ways to force your XAML to use a given OS theme. These theme definitions for WPF controls are contained in an embedded resource dictionary and can be used just like any other Resource Dictionary. You can read on how to do that in this article. It is worth noting that the results of forcefully using a Vista Resource Dictionary on XP does not yield the exact same results as actually being on Vista because of how the merging of dictionaries occurs. If I load my Resource Dictionary before the Vista one then all of my colors and styling for my labels, etc. is overridden and set back to black, and if I load the Vista Resource Dictionary first and then mine then I end up with only Vista styled buttons, and everything else appears the same. I can't imagine forcefully setting the OS theme to use is considered a good practice - otherwise wouldn't it be less cryptic to do? The moral of the story is to always check how your dialogs will look in other OS's unless your going to use custom templates.

This is the end of this series on Windows Forms in XAML. It was a good intro for me, and I hope maybe partially useful to someone somewhere. For now it's time to continue reading my WPF Unleashed book and start tackling more hurdles in learning WPF.