Introduction
I'm building a messaging application. This application is a SOA desktop application written in C# that uses WPF. At some point in time, I thought that it would be nice if my application had a black based theme like the one expression blend uses. I started with the scrollbar and this is what I'm going to talk about in this article: how to style a scrollbar so that it looks like the scrollbars in expression blend. I styled both the horizontal and vertical scrollbars. The downloadable code also includes styles for a listbox that uses both the scrollbars (so that the scrollbar would look better:)).
Background
I guess the reader should have a basic knowledge of styles and templates. If the reader is also going to read the styles for the list box, he/she should also know some basics about animations.
Brushes
The sample uses the following brushes:
<SolidColorBrush x:Key="StandardBorderBrush" Color="DarkGray"></SolidColorBrush>
<SolidColorBrush x:Key="StandardBrush" Color="LightGray"></SolidColorBrush>
<SolidColorBrush x:Key="PressedBrush" Color="Gray"></SolidColorBrush>
<SolidColorBrush x:Key="HoverBrush" Color="#fefefe"></SolidColorBrush>
<SolidColorBrush x:Key="GlyphBrush" Color="333333"></SolidColorBrush>
The Scroll Bar Structure
As most of us already know, a scrollbar is not as easy to style as a button for example. This is because the scrollbar has about 6 components that need to be addressed. You can see them in the picture below:
To properly style the scrollbar, I used styles for: the repeat buttons that move the data one line at a time (one style for each), a style for the thumb and a style for the transparent repeat buttons (one for both).
At the end, the scrollbars will look like the images below. On the left, you can see the entire window and on the right, you can see the 3 states of the scrollbar: normal, hover and pressed.
The Vertical Scroll Bar
This scrollbar is defined using a 3 row grid. The up button is on the first row, the track is on the second row and the down button is on the third row. The style for the up button can be seen in the following markup:
<Style x:Key="LineButtonUpStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Grid Margin="1" Height="18" >
<Path Stretch="None"
HorizontalAlignment="Center"
VerticalAlignment="Center" Name="Path"
Fill="{StaticResource
StandardBrush}"
Data="M0 0L 8 8 L 4 0 Z"></Path>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Path" Property="Fill"
Value="{StaticResource HoverBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Path" Property="Fill"
Value="{StaticResource PressedBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
As you can see, this button shows a path element that represents a triangle. This triangle is placed in the center of the button using the alignment properties. Also this button has 2 triggers. When the mouse hovers over the button, the triangle gets a lighter brush (HoverBrush
) and when the button is pressed, the triangle is given a darker brush (PressedBrush
).
The style for the down buton is very similar. The only difference is the orientation of the triangle. This one points down. The style can be seen below. The differences are in bold.
<Style x:Key="LineButtonDownStyle"
TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable"
Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type
RepeatButton}">
<Grid Margin="1"
Height="18" >
<Path Stretch="None" HorizontalAlignment="Center"
VerticalAlignment="Center" Name="Path" Fill="{StaticResource
StandardBrush}"
Data="M 0 0 L 4 8 L 8 0 Z"></Path></Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Path" Property="Fill"
Value="{StaticResource HoverBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Path" Property="Fill"
Value="{StaticResource PressedBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The style for the other 2 repeat buttons (the transparent ones) is very simple. It sets the template of the button to a transparent border. You can see this style below:
<Style x:Key="ScrollBarPageButtonStyle"
TargetType="{x:Type RepeatButton}">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Background="Transparent"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The style for the thumb can be seen below:
<Style x:Key="VerticalScrollBarThumbStyle"
TargetType="{x:Type Thumb}">
<Setter Property="IsTabStop"
Value="False"/>
<Setter Property="Focusable"
Value="False"/>
<Setter Property="Margin"
Value="1,0,1,0" />
<Setter Property="BorderBrush" Value="{StaticResource StandardBorderBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Rectangle Width="8" Name="ellipse" Stroke="{StaticResource
StandardBorderBrush}"
Fill="{StaticResource StandardBrush}"
RadiusX="5" RadiusY="5"></Rectangle>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="ellipse"
Property="Fill" Value="{StaticResource HoverBrush}"></Setter>
</Trigger>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="ellipse"
Property="Fill" Value="{StaticResource PressedBrush}"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
As you can see from the style above, the thumb is an 8 pixel wide rectangle named ellipse(because I originally wanted to use an ellipse and I didn’t change it). The thumb also has 2 triggers. When the mouse hovers over the thumb, the thumb gets a lighter brush (HoverBrush
). When the user drags the thumb, it gets a darker brush (PressedBrush
).
In order to build the scrollbar, all the above styles need to be used. Like I said before, the scrollbar is a three row grid that has a very subtle linear gradient background.
<ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="18"/>
<RowDefinition Height="*"/>
<RowDefinition MaxHeight="18"/>
</Grid.RowDefinitions>
<Grid.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#4c4c4c"></GradientStop>
<GradientStop Offset="1" Color="#434343"></GradientStop>
</LinearGradientBrush>
</Grid.Background>
On the first row, we have the up button.
<RepeatButton Grid.Row="0" Height="18"
Style="{StaticResource LineButtonUpStyle}"
Command="ScrollBar.LineUpCommand">
</RepeatButton>
As you can see, this is a repeat button that uses the LineButtonUpStyle
style discussed before (the one with the triangle pointing up).
On the second row, we have the track of the scrollbar. This track has to have a special name (PART_Track
).
<Track Name="PART_Track" Grid.Row="1" IsDirectionReversed="True">
<Track.DecreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageUpCommand"
Style="{StaticResource ScrollBarPageButtonStyle}">
</RepeatButton>
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource VerticalScrollBarThumbStyle}">
</Thumb>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageDownCommand"
Style="{StaticResource ScrollBarPageButtonStyle}">
</RepeatButton>
</Track.IncreaseRepeatButton>
</Track>
As you can see from the XAML above, the track has 3 parts. The decrease value part at the top, the thumb in the middle and the increase value part at the bottom. The increase and decrease parts use a repeat button that uses the transparent style. The thumb uses its own style described previously.
Here I need to mention something else: the IsDirectionReversed
property. It looks like if I don’t set this to true
, the thumb starts at the bottom of the scrollbar.
Finally, on the last row, we have another repeat button (the one with the triangle pointing down).
<RepeatButton Grid.Row="2" Height="18" Style="{StaticResource LineButtonDownStyle}"
Command="ScrollBar.LineDownCommand">
</RepeatButton>
The Horizontal Scroll Bar
The horizontal scrollbar has a similar structure. The XAML below shows you the markup for the template.
<ControlTemplate x:Key="HorizontalScrollBar"
TargetType="{x:Type ScrollBar}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="18"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition MaxWidth="18"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Offset="0" Color="#4c4c4c"></GradientStop>
<GradientStop Offset="1" Color="#434343"></GradientStop>
</LinearGradientBrush>
</Grid.Background>
<RepeatButton Grid.Column="0" Width="18"
Style="{StaticResource LineButtonLeftStyle}"
Command="ScrollBar.LineLeftCommand">
</RepeatButton>
<Track Name="PART_Track"
Grid.Column="1" IsDirectionReversed="False" >
<Track.DecreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageLeftCommand"
Style="{StaticResource
ScrollBarPageButtonStyle}">
</RepeatButton>
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource HorizontalScrollBarThumbStyle}">
</Thumb>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageRightCommand"
Style="{StaticResource ScrollBarPageButtonStyle}">
</RepeatButton>
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton Grid.Column="2" Width="18"
Style="{StaticResource
LineButtonRightStyle}"
Command="ScrollBar.LineRightCommand">
</RepeatButton>
</Grid>
</ControlTemplate>
As you can see, this time we have a 3 column grid. The first column holds the scroll left repeat button, the second column contains the track and the last column contains the scroll right repeat button. The styles used here are almost the same with the ones used for the vertical scrollbar. The triggers are exactly the same. The only differences are the orientation of the triangles (they point left and right), and the dimensions of the thumb rectangle. One last thing to notice here is that the IsDirectionReversed
property is left to false
.
The Scroll Bar Style
These 2 templates will be used to set the template for the scrollbar. This is applied using a style. You can see this below:
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Style.Triggers>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Width" Value="18"/>
<Setter Property="Height" Value="Auto" />
<Setter Property="Template" Value="{StaticResource VerticalScrollBar}" />
</Trigger>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Width" Value="Auto"/>
<Setter Property="Height" Value="18" />
<Setter Property="Template" Value="{StaticResource HorizontalScrollBar}" />
</Trigger>
</Style.Triggers></Style>
As you can see from the above XAML, if the orientation is vertical the style for the vertical scrollbar will use the VerticalScrollBar
template and if the orientation is horizontal the HorizontalScrollBar
template will be used. One last thing to mention is that I chose to use separate templates for the 4 scroll buttons (left, right, up and down) because the brush for the triangles needs to change. If, on the other hand,the background of the buttons had to change this could have been done with a single template like the transparent buttons.
Something About the List Box Style
Also please check out the styles for the list box because they contain something very cool. A way to style the rectangle in the bottom right corner of the listbox (the one at the intersection of the 2 scrollbars).
If you look at a default template of a scroll viewer, you will notice that this rectangle is named ‘corner’ and uses a brush with the following key:
x:Key="{x:Static SystemColors.ControlBrushKey}"
The template adds a resource with the same name in order to override the default color. This takes advantage of the way WPF searches for resources: from the element outward. This is it. Hope you like the article.
History
- Added on Wednesday, 26 August, 2009