Tuesday, January 15, 2013

WPF ScrollBar Control Style


How to style a WPF ScrollBar control?

The WPF ScrollBar control is the control that appears most likely (in all containers like ScrollViewer, ListBox, ListView and Grid) everytime the children or the content overlaps on its container.

In WPF, the visual tree implementation of the controls were actually to reuse the existing implemented controls (and that is what happened in ScrollBar control). Hence this control is a separate object/control, considered as a separate FrameworkElement, then you can do your own style and design for this. All you should know are, what PARTS and what type of control the PART is using when composing the ScrollBar control.

ScrollBar Control PARTS

For your reference you can visit the Microsoft documentation.

One common PART you need to remember every time you design the ScrollBar control is the PART_Track of type Track object. The ScrollBar Track is the one you'd see in the control where you adjust the offset of the Thumb depends on the content of the container (Vertical Offset, Content Height).

Below is the image of un-styled ScrollBar control.


The Track control contains 3 main property that are very important for the ScrollBar control. Those property are DecreaseRepeatButtonThumb and IncreaseRepeatButton.

DecreaseRepeatButton: is the part of the Track that is clickable that commands the value of the offset should be decreased.

Thumb: is the part of the Track that is draggable that commands whether to increase/decrease the offset value (depends on the drag direction).

IncreaseRepeatButton: is the part of the Track that is clickable that commands the value of the offset should be increased.

Now, if you wish to modify the style of each control, you should create your own Style object for every control found in the template. The candidate controls for styling are RepeatButton and Thumb.

Styling the Thumb

This is a very simple to do because Thumb control is a composite control and you can template it by what control would you like. In our case, we used the Border object to style it.

Here is the XAML of my design.

<Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="IsTabStop" Value="false"/>
    <Setter Property="Focusable" Value="false"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
                <Border CornerRadius="3"
                        Background="#AFC7D8"
                        BorderBrush="#A7B4BC"
                        BorderThickness="0" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Styling the RepeatButton

Same with the Thumb control, this is a very simple to design. But for our design we have to make it at transparent Background since we don't want this control take over the colors and design of the other controls.

Here is the XAML of our design.

<Style x:Key="ScrollBarPageButton" TargetType="{x:Type RepeatButton}">
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <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>

Once you've done with your RepeatButton and Thumb styles. Then you can template the Track object.

Customizing the Template of the Track

The Track buttons should be bound with the actual command button the ScrollBar object is using. For the case of DecreaseRepeatButton, you should bind it to PageLeftCommand of the ScrollBar and for the case IncreaseRepeatButton you should bind it to the PageRightCommand of the ScrollBar.

See below the XAML that can be use to template the Track. It has applied the style of the Thumb and RepeatButton designed above.

<ControlTemplate x:Key="HorizontalScrollBar" TargetType="{x:Type ScrollBar}">
    <Grid Height="8">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.00001*"/>
        </Grid.ColumnDefinitions>
        <Border BorderBrush="#D0E2F2"
                BorderThickness="1,1,0,0"
                CornerRadius="4"
                Background="#DBEBF8" />
        <Track Name="PART_Track"
                Grid.Column="1"
                IsDirectionReversed="False">
            <Track.DecreaseRepeatButton>
                <RepeatButton Style="{StaticResource ScrollBarPageButton}"
                              Command="ScrollBar.PageLeftCommand" />
            </Track.DecreaseRepeatButton>
            <Track.Thumb>
                <Thumb Style="{StaticResource ScrollBarThumb}"
                        Margin="0,1,0,1"
                        Background="{StaticResource NormalBrush}"
                        BorderBrush="{StaticResource NormalBorderBrush}" />
            </Track.Thumb>
            <Track.IncreaseRepeatButton>
                <RepeatButton Style="{StaticResource ScrollBarPageButton}"
                              Command="ScrollBar.PageRightCommand" />
            </Track.IncreaseRepeatButton>
        </Track>
    </Grid>
</ControlTemplate>

You will notice some bound variables like NormalBrush and NormalBorderBrush. You can place me XAML below of the top of your ResourceDictionary object. See below there XAML.

<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
    <GradientBrush.GradientStops>
        <GradientStopCollection>
            <GradientStop Color="#FFF" Offset="0.0"/>
            <GradientStop Color="#CCC" Offset="1.0"/>
        </GradientStopCollection>
    </GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
    <GradientBrush.GradientStops>
        <GradientStopCollection>
            <GradientStop Color="#CCC" Offset="0.0"/>
            <GradientStop Color="#444" Offset="1.0"/>
        </GradientStopCollection>
    </GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ScrollBarNormalBrush" StartPoint="0,0" EndPoint="1,0">
    <GradientBrush.GradientStops>
        <GradientStopCollection>
            <GradientStop Color="#FFF" Offset="0.0"/>
            <GradientStop Color="#CCC" Offset="1.0"/>
        </GradientStopCollection>
    </GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ScrollBarBorderBrush" StartPoint="0,0" EndPoint="1,0">
    <GradientBrush.GradientStops>
        <GradientStopCollection>
            <GradientStop Color="#CCC" Offset="0.0"/>
            <GradientStop Color="#444" Offset="1.0"/>
        </GradientStopCollection>
    </GradientBrush.GradientStops>
</LinearGradientBrush>

Here is the XAML to be applied in the ScrollBar control that actually implements the Styles mentioned above.

<Style x:Key="styleScrollBar" TargetType="{x:Type ScrollBar}">
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Style.Triggers>
        <Trigger Property="Orientation" Value="Horizontal">
            <Setter Property="Width" Value="Auto"/>
            <Setter Property="Height" Value="18" />
            <Setter Property="Template" Value="{StaticResource HorizontalScrollBar}" />
        </Trigger>
        <Trigger Property="Orientation" Value="Vertical">
            <Setter Property="Width" Value="18"/>
            <Setter Property="Height" Value="Auto" />
            <Setter Property="Template" Value="{StaticResource VerticalScrollBar}" />
        </Trigger>
    </Style.Triggers>
</Style>

Applying the Style in the ScrollBar

Same of what I have discussed on earlier topics, you should place all the XAML codes you have made in one ResourceDictionary. Set the Build Action to Resource and reference the ResourceDictionary in your Window or the container of your ScrollBar.

See below the XAML referencing the ScrollBar ResourceDictionary residing in one "Styles" folder of my solution.

<Window x:Class="CodesDirectory.WIN_ScrollBar"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF ScrollBar" Height="300" Width="300">
    <Window.Resources>
        <ResourceDictionary Source="/Styles/ScrollBar.xaml"></ResourceDictionary>
    </Window.Resources>
    <Grid>
        <ScrollBar Margin="10"
                   Orientation="Horizontal"
                   VerticalAlignment="Stretch"
                   Style="{DynamicResource ResourceKey=styleScrollBar}">
        </ScrollBar>
    </Grid>
</Window>

See below the actual output of our design.


Hollah! You are now done designing your own ScrollBar. ;)

5 comments:

Place your comments and ideas