Click here to Skip to main content
15,896,726 members
Articles / Desktop Programming / WPF

WPF Custom ListBox with Scrollbar on the Background

Rate me:
Please Sign up or sign in to vote.
4.90/5 (18 votes)
9 Mar 2010CPOL5 min read 100.5K   6.6K   36  
This article explains how to create a ListBox in WPF with the scrollbar in the background and auto-scrolling functionality when hovering over the control ends.
<UserControl x:Class="WpfControls.ListBoxWithAutoScroll"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfControls"
    >
    <UserControl.Resources>
        <!-- Fill Brushes -->

        <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="HorizontalNormalBrush" 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="LightBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#AAA" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#BBB" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="0.1"/>
                    <GradientStop Color="#EEE" Offset="0.9"/>
                    <GradientStop Color="#FFF" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

        <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />

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

        <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />

        <!-- Border Brushes -->

        <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="HorizontalNormalBorderBrush" 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>

        <LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#777" Offset="0.0"/>
                    <GradientStop Color="#000" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#444" Offset="0.0"/>
                    <GradientStop Color="#888" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />

        <SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />

        <SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />

        <!-- Miscellaneous Brushes -->

        <SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />
        
        <Style x:Key="ScrollBarLineButton" TargetType="{x:Type RepeatButton}">
            <Setter Property="SnapsToDevicePixels" Value="True"/>
            <Setter Property="OverridesDefaultStyle" Value="true"/>
            <Setter Property="Focusable" Value="false"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type RepeatButton}">
                        <Border Name="Border" CornerRadius="2"  Background="{StaticResource NormalBrush}"
                              BorderBrush="{StaticResource NormalBorderBrush}" >
                            <Path HorizontalAlignment="Center" VerticalAlignment="Center" Fill="Gray"
                                Data="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" 
                                Opacity="{Binding Path=Opacity, RelativeSource={RelativeSource TemplatedParent}}"  />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <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>

        <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="2" Background="{TemplateBinding Background}" 
                                BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    
        <!--listBoxWithAutoScroll_Horizontal--> 
        <Style x:Key="listBoxWithAutoScroll_Horizontal" TargetType="{x:Type ListBox}" >
            <Setter Property="SnapsToDevicePixels" Value="true"/>
            <Setter Property="OverridesDefaultStyle" Value="true"/>
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
            <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
            <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="Template">
                <Setter.Value >
                    <ControlTemplate>
                        <Grid>      
                        <ScrollViewer x:Name="scrollviewer" >
                                <ScrollViewer.Template>
                                    <ControlTemplate TargetType="{x:Type ScrollViewer}" >
                                        <Grid>
                                            <ScrollBar x:Name="PART_HorizontalScrollBar" Orientation="Horizontal"
                                                               Value="{TemplateBinding HorizontalOffset}"
                                                               Maximum="{TemplateBinding ScrollableWidth}"
                                                               ViewportSize="{TemplateBinding ViewportWidth}"
                                                               Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
                                                               Height="{Binding Height, ElementName=Panel}">
                                                <ScrollBar.Template>
                                                    <ControlTemplate>
                                                        <Grid Background="{TemplateBinding Background}" >
                                                            <Track x:Name="PART_Track">
                                                                <Track.DecreaseRepeatButton>
                                                                    <RepeatButton Command="ScrollBar.PageLeftCommand"
						                                                    Style="{StaticResource ScrollBarPageButton}"/>
                                                                </Track.DecreaseRepeatButton>
                                                                <Track.IncreaseRepeatButton>
                                                                    <RepeatButton Command="ScrollBar.PageRightCommand"
						                                                    Style="{StaticResource ScrollBarPageButton}"/>
                                                                </Track.IncreaseRepeatButton>
                                                                <Track.Thumb>
                                                                    <Thumb Style="{StaticResource ScrollBarThumb}" 
                                                                         Background="Gray" Opacity="0.5" />
                                                                </Track.Thumb>
                                                            </Track>
                                                        </Grid>
                                                    </ControlTemplate>
                                                </ScrollBar.Template>
                                            </ScrollBar>
                                            <ScrollContentPresenter Margin="0,4" Height="Auto" VerticalAlignment="Center"/>
                                        </Grid>
                                    </ControlTemplate>
                                </ScrollViewer.Template>
                                <ItemsPresenter/>
                            </ScrollViewer>
                            <Grid x:Name="Panel" Margin="0,1">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <RepeatButton x:Name="LineLeftButton" Grid.Column="0" Width="20" Opacity="0"
                                      Style="{StaticResource ScrollBarLineButton}"
                                      Content="M 8 0 L 8 32 L 0 16 Z"       
                                      Command="{x:Static ScrollBar.LineLeftCommand}"       
                                      CommandTarget="{Binding ElementName=scrollviewer}"
                                      ClickMode="Hover" />
                                <RepeatButton x:Name="LineRightButton" Grid.Column="2" Width="20" Opacity="0"
                                      Style="{StaticResource ScrollBarLineButton}"
                                      Content="M 0 0 L 8 16 L 0 32 Z" 
                                      Command="{x:Static ScrollBar.LineRightCommand}"       
                                      CommandTarget="{Binding ElementName=scrollviewer}"
                                      ClickMode="Hover"/>
                            </Grid>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                    <Condition Property="ComputedHorizontalScrollBarVisibility" SourceName="scrollviewer" Value="Visible"/>
                                </MultiTrigger.Conditions>
                                <!--<Setter TargetName="LineLeftButton" Property="Visibility" Value="Visible"/>
                                <Setter TargetName="LineRightButton" Property="Visibility" Value="Visible"/>-->
                                <MultiTrigger.EnterActions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <DoubleAnimation Storyboard.TargetName="LineLeftButton" 
                                                             Storyboard.TargetProperty="Opacity" To="0.8" Duration="0:0:0.25"/>
                                            <DoubleAnimation Storyboard.TargetName="LineRightButton" 
                                                             Storyboard.TargetProperty="Opacity" To="0.8" Duration="0:0:0.25"/>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </MultiTrigger.EnterActions>
                                <MultiTrigger.ExitActions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <DoubleAnimation Storyboard.TargetName="LineLeftButton" 
                                                             Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.25"/>
                                            <DoubleAnimation Storyboard.TargetName="LineRightButton" 
                                                             Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.25"/>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </MultiTrigger.ExitActions>
                            </MultiTrigger>                      
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemsPanel">
                <Setter.Value >
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="listBoxWithAutoScroll_Vertical" TargetType="{x:Type ListBox}" >
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="Template">
                <Setter.Value >
                    <ControlTemplate>
                        <DockPanel x:Name="Panel">
                            <RepeatButton x:Name="LineUpButton" DockPanel.Dock="Top" Height="20"  
                                      
                                      Content="M 0 4 L 8 4 L 4 0 Z"     
                                      Command="{x:Static ScrollBar.LineUpCommand}"       
                                      CommandTarget="{Binding ElementName=scrollviewer}"
                                      ClickMode="Hover" />
                            <RepeatButton x:Name="LineDownButton" DockPanel.Dock="Bottom" Height="20" 
                                      
                                      Content="M 0 0 L 4 4 L 8 0 Z" 
                                      Command="{x:Static ScrollBar.LineDownCommand}"       
                                      CommandTarget="{Binding ElementName=scrollviewer}"
                                      ClickMode="Hover"/>
                            <Border BorderThickness="1" BorderBrush="Gray" Background="Transparent">
                                <ScrollViewer x:Name="scrollviewer"  >
                                    <ScrollViewer.Template>
                                        <ControlTemplate TargetType="{x:Type ScrollViewer}" >
                                            <Grid>
                                                <ScrollBar x:Name="PART_VerticalScrollBar" Orientation="Vertical" 
                                                               Value="{TemplateBinding VerticalOffset}"
                                                               Maximum="{TemplateBinding ScrollableHeight}"
                                                               ViewportSize="{TemplateBinding ViewportHeight}"
                                                               Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
                                                               Width="{Binding Width, ElementName=Panel}">
                                                    <ScrollBar.Template>
                                                        <ControlTemplate>
                                                            <Grid Background="{TemplateBinding Background}" >
                                                                <Track x:Name="PART_Track" IsDirectionReversed="True">
                                                                    <Track.DecreaseRepeatButton>
                                                                        <RepeatButton Command="ScrollBar.PageUpCommand"
						                                                    Style="{StaticResource ScrollBarPageButton}"/>
                                                                    </Track.DecreaseRepeatButton>
                                                                    <Track.IncreaseRepeatButton>
                                                                        <RepeatButton Command="ScrollBar.PageDownCommand"
						                                                    Style="{StaticResource ScrollBarPageButton}"/>
                                                                    </Track.IncreaseRepeatButton>
                                                                    <Track.Thumb>
                                                                        <Thumb Background="Gray" Opacity="0.1" />
                                                                    </Track.Thumb>
                                                                </Track>
                                                            </Grid>
                                                        </ControlTemplate>
                                                    </ScrollBar.Template>
                                                </ScrollBar>
                                                <ScrollContentPresenter Margin="4,0" Width="Auto" HorizontalAlignment="Center"/>
                                            </Grid>
                                        </ControlTemplate>
                                    </ScrollViewer.Template>
                                    <ItemsPresenter/>
                                </ScrollViewer>
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemsPanel">
                <Setter.Value >
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel Orientation="Vertical"/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        
    </UserControl.Resources>

    <ListBox x:Name="listBox" Style="{StaticResource listBoxWithAutoScroll_Horizontal}">


    </ListBox>
</UserControl>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Pfizer/MSolutions
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions