Custom ScrollBars in WPF

The following post contains a basic example of how to customise the WPF ScrollView scroll bars. The original XAML was built using Expression Blend, and has been further simplified for clarity. The resulting ScrollView looks like this:

Firstly, the ScrollViewerControlTemplate must be overridden to allow the background image to bleed under the scroll bars:

<ControlTemplate x:Key="ScrollViewerControlTemplate" TargetType="{x:Type ScrollViewer}">
	<Grid x:Name="Grid" Background="{TemplateBinding Background}">
		<Grid.ColumnDefinitions>
			<ColumnDefinition/>
			<ColumnDefinition Width="Auto"/>
		</Grid.ColumnDefinitions>
		<Grid.RowDefinitions>
			<RowDefinition />
			<RowDefinition Height="Auto"/>
		</Grid.RowDefinitions>
		<Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
		<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.RowSpan="2" Margin="{TemplateBinding Padding}" Grid.ColumnSpan="2"/>
		<ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" Width="8"/>
		<ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Height="8"/>
	</Grid>
</ControlTemplate>

Next, the ScrollBarPageButton style is modified to have a transparent background:

<Style x:Key="ScrollBarPageButton" TargetType="{x:Type RepeatButton}">
	<Setter Property="OverridesDefaultStyle" Value="true"/>
	<Setter Property="Background" Value="Transparent"/>
	<Setter Property="Focusable" Value="false"/>
	<Setter Property="IsTabStop" Value="false"/>
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="{x:Type RepeatButton}">
				<Rectangle Fill="{TemplateBinding Background}" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}"/>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>

In order for the thumb to render correctly in either orientation, two styles are needed. The horizontal…:

<Style x:Key="HorizontalThumb" TargetType="{x:Type Thumb}">
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="{x:Type Thumb}">
				<Grid>
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="4" />
						<ColumnDefinition Width="4" />
						<ColumnDefinition />
						<ColumnDefinition Width="4" />
						<ColumnDefinition Width="4" />
					</Grid.ColumnDefinitions>
					<Ellipse Grid.Column="0" Grid.ColumnSpan="2" Fill="#FFF4F4F5" />
					<Rectangle Grid.Column="1" Grid.ColumnSpan="3" Fill="#FFF4F4F5" />
					<Ellipse Grid.Column="3" Grid.ColumnSpan="2" Fill="#FFF4F4F5" />
				</Grid>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>

…and the vertical:

<Style x:Key="VerticalThumb" TargetType="{x:Type Thumb}">
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="{x:Type Thumb}">
				<Grid>
					<Grid.RowDefinitions>
						<RowDefinition Height="4" />
						<RowDefinition Height="4" />
						<RowDefinition />
						<RowDefinition Height="4" />
						<RowDefinition Height="4" />
					</Grid.RowDefinitions>
					<Ellipse Grid.Row="0" Grid.RowSpan="2" Fill="#FFF4F4F5" />
					<Rectangle Grid.Row="1" Grid.RowSpan="3" Fill="#FFF4F4F5" />
					<Ellipse Grid.Row="3" Grid.RowSpan="2" Fill="#FFF4F4F5" />
				</Grid>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>

The ScrollBar width is set to 10px, with an 8px wide thumb. This allows a single pixel of padding on either side. The Decrease/Increase buttons have been deleted. The reset of the XAML is auto-generated by Expression Blend:

<Style TargetType="{x:Type ScrollBar}">
	<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
	<Setter Property="Stylus.IsFlicksEnabled" Value="false"/>
	<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
	<Setter Property="Width" Value="10"/>
	<Setter Property="MinWidth" Value="10"/>
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="{x:Type ScrollBar}">
				<Grid x:Name="Bg" SnapsToDevicePixels="true" Background="#7FBFBFBF">
					<Grid.RowDefinitions>
						<RowDefinition Height="0.00001*"/>
					</Grid.RowDefinitions>
					<Track x:Name="PART_Track" IsDirectionReversed="true" IsEnabled="{TemplateBinding IsMouseOver}" Grid.Row="1">
						<Track.DecreaseRepeatButton>
							<RepeatButton Command="{x:Static ScrollBar.PageUpCommand}" Style="{StaticResource ScrollBarPageButton}"/>
						</Track.DecreaseRepeatButton>
						<Track.IncreaseRepeatButton>
							<RepeatButton Command="{x:Static ScrollBar.PageDownCommand}" Style="{StaticResource ScrollBarPageButton}"/>
						</Track.IncreaseRepeatButton>
						<Track.Thumb>
							<Thumb Style="{DynamicResource VerticalThumb}" Microsoft_Windows_Themes:ScrollChrome.ScrollGlyph="VerticalGripper" Width="8"/>
						</Track.Thumb>
					</Track>
				</Grid>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
	<Style.Triggers>
		<Trigger Property="Orientation" Value="Horizontal">
			<Setter Property="Width" Value="Auto"/>
			<Setter Property="MinWidth" Value="0"/>
			<Setter Property="Height" Value="10"/>
			<Setter Property="MinHeight" Value="10"/>
			<Setter Property="Template">
				<Setter.Value>
					<ControlTemplate TargetType="{x:Type ScrollBar}">
						<Grid x:Name="Bg" SnapsToDevicePixels="true" Background="#7FA7A7A7">
							<Grid.ColumnDefinitions>
								<ColumnDefinition Width="0.00001*"/>
							</Grid.ColumnDefinitions>
							<Track x:Name="PART_Track" Grid.Column="1" IsEnabled="{TemplateBinding IsMouseOver}">
								<Track.DecreaseRepeatButton>
									<RepeatButton Command="{x:Static ScrollBar.PageLeftCommand}" Style="{StaticResource ScrollBarPageButton}"/>
								</Track.DecreaseRepeatButton>
								<Track.IncreaseRepeatButton>
									<RepeatButton Command="{x:Static ScrollBar.PageRightCommand}" Style="{StaticResource ScrollBarPageButton}"/>
								</Track.IncreaseRepeatButton>
								<Track.Thumb>
									<Thumb Microsoft_Windows_Themes:ScrollChrome.ScrollGlyph="HorizontalGripper" Style="{DynamicResource HorizontalThumb}" Height="8"/>
								</Track.Thumb>
							</Track>
						</Grid>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Trigger>
	</Style.Triggers>
</Style>

Finally, the main window XAML should look resemble this:

<Grid x:Name="LayoutRoot">
	<ScrollViewer Margin="8" HorizontalScrollBarVisibility="Auto" Template="{DynamicResource ScrollViewerControlTemplate}">
		<Image Source="pack://siteoforigin:,,,/Tulips.jpg" Width="1024" Height="768" />
	</ScrollViewer>
</Grid>

Resources:
Will it Blend: Custom Scrollbar in WPF
Pluralsight: Expression Blend for Developers

Advertisements
This entry was posted in Reference and tagged , . Bookmark the permalink.

5 Responses to Custom ScrollBars in WPF

  1. rekha says:

    I’m searching for styling scroll bar , this content posted here is helpful . Thanks for posting.

  2. Thank you. This article was helpful. For anybody getting errors about namespace Please add a reference to any themes in PresentationFramework like PresentationFramework.Aero and add the following namespace xmlns:Microsoft_Windows_Themes=”clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero”

  3. Anas says:

    Many many thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s