WPF控件样式和模板全解析-ScrollViewer控件

ScrollViewer和ScrollBar

ScrollViewer控件也是一个内容控件,但是它和前面介绍的Button、Label等内容控件不太一样。

它更像一个容器,用于创建可滚动区域。一个 ScrollViewer 只能有一个子元素,通常会使用一个面板来做为ScrollViewer的子元素。

demo

ScrollViewer控件包含了水平和垂直两部分的滚动条区域,这两个滚动条区域是由ScrollBar控件来实现。

如下所示:

image

所以本文会把ScrollViewer和ScrollBar两个控件的样式放在一起来进行讲解。

 

ScrollViewer默认样式和模板

下面依旧是使用Blend来查看ScrollViewer的默认样式和模板

 1     <Style x:Key="ScrollViewerStyle1" TargetType="{x:Type ScrollViewer}">
 2         <Setter Property="Template">
 3             <Setter.Value>
 4                 <ControlTemplate TargetType="{x:Type ScrollViewer}">
 5                     <Grid x:Name="Grid" Background="{TemplateBinding Background}">
 6                         <Grid.ColumnDefinitions>
 7                             <ColumnDefinition Width="*"/>
 8                             <ColumnDefinition Width="Auto"/>
 9                         </Grid.ColumnDefinitions>
10                         <Grid.RowDefinitions>
11                             <RowDefinition Height="*"/>
12                             <RowDefinition Height="Auto"/>
13                         </Grid.RowDefinitions>
14                         <Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
15                         <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanHorizontallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" CanVerticallyScroll="False" Grid.Column="0" Content="{TemplateBinding Content}" CanContentScroll="{TemplateBinding CanContentScroll}" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
16                         <ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
17                         <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
18                     </Grid>
19                 </ControlTemplate>
20             </Setter.Value>
21         </Setter>
22         <Style.Triggers>
23             <Trigger Property="IsEnabled" Value="false">
24                 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
25             </Trigger>
26         </Style.Triggers>
27     </Style>

 

ScrollBar的默认样式和模板

ScrollBar的默认样式和模板

  1 <Style x:Key="FocusVisual">
  2     <Setter Property="Control.Template">
  3         <Setter.Value>
  4             <ControlTemplate>
  5                 <Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
  6             </ControlTemplate>
  7         </Setter.Value>
  8     </Setter>
  9 </Style>
 10 <SolidColorBrush x:Key="ScrollBar.Static.Background" Color="#F0F0F0"/>
 11 <SolidColorBrush x:Key="ScrollBar.Static.Border" Color="#F0F0F0"/>
 12 <SolidColorBrush x:Key="ScrollBar.Static.Glyph" Color="#606060"/>
 13 <SolidColorBrush x:Key="ScrollBar.Static.Thumb" Color="#CDCDCD"/>
 14 <SolidColorBrush x:Key="ScrollBar.MouseOver.Background" Color="#DADADA"/>
 15 <SolidColorBrush x:Key="ScrollBar.MouseOver.Border" Color="#DADADA"/>
 16 <SolidColorBrush x:Key="ScrollBar.MouseOver.Glyph" Color="#000000"/>
 17 <SolidColorBrush x:Key="ScrollBar.MouseOver.Thumb" Color="#A6A6A6"/>
 18 <SolidColorBrush x:Key="ScrollBar.Pressed.Background" Color="#606060"/>
 19 <SolidColorBrush x:Key="ScrollBar.Pressed.Border" Color="#606060"/>
 20 <SolidColorBrush x:Key="ScrollBar.Pressed.Thumb" Color="#606060"/>
 21 <SolidColorBrush x:Key="ScrollBar.Pressed.Glyph" Color="#FFFFFF"/>
 22 <SolidColorBrush x:Key="ScrollBar.Disabled.Background" Color="#F0F0F0"/>
 23 <SolidColorBrush x:Key="ScrollBar.Disabled.Border" Color="#F0F0F0"/>
 24 <SolidColorBrush x:Key="ScrollBar.Disabled.Glyph" Color="#BFBFBF"/>
 25 <Style x:Key="RepeatButtonTransparent" TargetType="{x:Type RepeatButton}">
 26     <Setter Property="OverridesDefaultStyle" Value="true"/>
 27     <Setter Property="Background" Value="Transparent"/>
 28     <Setter Property="Focusable" Value="false"/>
 29     <Setter Property="IsTabStop" Value="false"/>
 30     <Setter Property="Template">
 31         <Setter.Value>
 32             <ControlTemplate TargetType="{x:Type RepeatButton}">
 33                 <Rectangle Fill="{TemplateBinding Background}" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}"/>
 34             </ControlTemplate>
 35         </Setter.Value>
 36     </Setter>
 37 </Style>
 38 <Style x:Key="ScrollBarButton" TargetType="{x:Type RepeatButton}">
 39     <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
 40     <Setter Property="BorderThickness" Value="1"/>
 41     <Setter Property="HorizontalContentAlignment" Value="Center"/>
 42     <Setter Property="VerticalContentAlignment" Value="Center"/>
 43     <Setter Property="Padding" Value="1"/>
 44     <Setter Property="Focusable" Value="false"/>
 45     <Setter Property="IsTabStop" Value="false"/>
 46     <Setter Property="Template">
 47         <Setter.Value>
 48             <ControlTemplate TargetType="{x:Type RepeatButton}">
 49                 <Border x:Name="border" Background="{StaticResource ScrollBar.Static.Background}" BorderBrush="{StaticResource ScrollBar.Static.Border}" BorderThickness="1" SnapsToDevicePixels="true">
 50                     <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
 51                 </Border>
 52                 <ControlTemplate.Triggers>
 53                     <Trigger Property="IsMouseOver" Value="true">
 54                         <Setter Property="Background" TargetName="border" Value="{StaticResource ScrollBar.MouseOver.Background}"/>
 55                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource ScrollBar.MouseOver.Border}"/>
 56                     </Trigger>
 57                     <Trigger Property="IsPressed" Value="true">
 58                         <Setter Property="Background" TargetName="border" Value="{StaticResource ScrollBar.Pressed.Background}"/>
 59                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource ScrollBar.Pressed.Border}"/>
 60                     </Trigger>
 61                     <Trigger Property="IsEnabled" Value="false">
 62                         <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
 63                         <Setter Property="Background" TargetName="border" Value="{StaticResource ScrollBar.Disabled.Background}"/>
 64                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource ScrollBar.Disabled.Border}"/>
 65                     </Trigger>
 66                 </ControlTemplate.Triggers>
 67             </ControlTemplate>
 68         </Setter.Value>
 69     </Setter>
 70 </Style>
 71 <Style x:Key="ScrollBarThumbVertical" TargetType="{x:Type Thumb}">
 72     <Setter Property="OverridesDefaultStyle" Value="true"/>
 73     <Setter Property="IsTabStop" Value="false"/>
 74     <Setter Property="Template">
 75         <Setter.Value>
 76             <ControlTemplate TargetType="{x:Type Thumb}">
 77                 <Rectangle x:Name="rectangle" Fill="{StaticResource ScrollBar.Static.Thumb}" Height="{TemplateBinding Height}" SnapsToDevicePixels="True" Width="{TemplateBinding Width}"/>
 78                 <ControlTemplate.Triggers>
 79                     <Trigger Property="IsMouseOver" Value="true">
 80                         <Setter Property="Fill" TargetName="rectangle" Value="{StaticResource ScrollBar.MouseOver.Thumb}"/>
 81                     </Trigger>
 82                     <Trigger Property="IsDragging" Value="true">
 83                         <Setter Property="Fill" TargetName="rectangle" Value="{StaticResource ScrollBar.Pressed.Thumb}"/>
 84                     </Trigger>
 85                 </ControlTemplate.Triggers>
 86             </ControlTemplate>
 87         </Setter.Value>
 88     </Setter>
 89 </Style>
 90 <Style x:Key="ScrollBarThumbHorizontal" TargetType="{x:Type Thumb}">
 91     <Setter Property="OverridesDefaultStyle" Value="true"/>
 92     <Setter Property="IsTabStop" Value="false"/>
 93     <Setter Property="Template">
 94         <Setter.Value>
 95             <ControlTemplate TargetType="{x:Type Thumb}">
 96                 <Rectangle x:Name="rectangle" Fill="{StaticResource ScrollBar.Static.Thumb}" Height="{TemplateBinding Height}" SnapsToDevicePixels="True" Width="{TemplateBinding Width}"/>
 97                 <ControlTemplate.Triggers>
 98                     <Trigger Property="IsMouseOver" Value="true">
 99                         <Setter Property="Fill" TargetName="rectangle" Value="{StaticResource ScrollBar.MouseOver.Thumb}"/>
100                     </Trigger>
101                     <Trigger Property="IsDragging" Value="true">
102                         <Setter Property="Fill" TargetName="rectangle" Value="{StaticResource ScrollBar.Pressed.Thumb}"/>
103                     </Trigger>
104                 </ControlTemplate.Triggers>
105             </ControlTemplate>
106         </Setter.Value>
107     </Setter>
108 </Style>
109 <Style x:Key="ScrollBarStyle1" TargetType="{x:Type ScrollBar}">
110     <Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
111     <Setter Property="Stylus.IsFlicksEnabled" Value="false"/>
112     <Setter Property="Background" Value="{StaticResource ScrollBar.Static.Background}"/>
113     <Setter Property="BorderBrush" Value="{StaticResource ScrollBar.Static.Border}"/>
114     <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
115     <Setter Property="BorderThickness" Value="1,0"/>
116     <Setter Property="Width" Value="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"/>
117     <Setter Property="MinWidth" Value="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"/>
118     <Setter Property="Template">
119         <Setter.Value>
120             <ControlTemplate TargetType="{x:Type ScrollBar}">
121                 <Grid x:Name="Bg" SnapsToDevicePixels="true">
122                     <Grid.RowDefinitions>
123                         <RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}"/>
124                         <RowDefinition Height="0.00001*"/>
125                         <RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}"/>
126                     </Grid.RowDefinitions>
127                     <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.Row="1"/>
128                     <RepeatButton x:Name="PART_LineUpButton" Command="{x:Static ScrollBar.LineUpCommand}" IsEnabled="{TemplateBinding IsMouseOver}" Style="{StaticResource ScrollBarButton}">
129                         <Path x:Name="ArrowTop" Data="M 0,4 C0,4 0,6 0,6 0,6 3.5,2.5 3.5,2.5 3.5,2.5 7,6 7,6 7,6 7,4 7,4 7,4 3.5,0.5 3.5,0.5 3.5,0.5 0,4 0,4 z" Fill="{StaticResource ScrollBar.Static.Glyph}" Margin="3,4,3,3" Stretch="Uniform"/>
130                     </RepeatButton>
131                     <Track x:Name="PART_Track" IsDirectionReversed="true" IsEnabled="{TemplateBinding IsMouseOver}" Grid.Row="1">
132                         <Track.DecreaseRepeatButton>
133                             <RepeatButton Command="{x:Static ScrollBar.PageUpCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
134                         </Track.DecreaseRepeatButton>
135                         <Track.IncreaseRepeatButton>
136                             <RepeatButton Command="{x:Static ScrollBar.PageDownCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
137                         </Track.IncreaseRepeatButton>
138                         <Track.Thumb>
139                             <Thumb Style="{StaticResource ScrollBarThumbVertical}"/>
140                         </Track.Thumb>
141                     </Track>
142                     <RepeatButton x:Name="PART_LineDownButton" Command="{x:Static ScrollBar.LineDownCommand}" IsEnabled="{TemplateBinding IsMouseOver}" Grid.Row="2" Style="{StaticResource ScrollBarButton}">
143                         <Path x:Name="ArrowBottom" Data="M 0,2.5 C0,2.5 0,0.5 0,0.5 0,0.5 3.5,4 3.5,4 3.5,4 7,0.5 7,0.5 7,0.5 7,2.5 7,2.5 7,2.5 3.5,6 3.5,6 3.5,6 0,2.5 0,2.5 z" Fill="{StaticResource ScrollBar.Static.Glyph}" Margin="3,4,3,3" Stretch="Uniform"/>
144                     </RepeatButton>
145                 </Grid>
146                 <ControlTemplate.Triggers>
147                     <MultiDataTrigger>
148                         <MultiDataTrigger.Conditions>
149                             <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineDownButton}" Value="true"/>
150                             <Condition Binding="{Binding IsPressed, ElementName=PART_LineDownButton}" Value="true"/>
151                         </MultiDataTrigger.Conditions>
152                         <Setter Property="Fill" TargetName="ArrowBottom" Value="{StaticResource ScrollBar.Pressed.Glyph}"/>
153                     </MultiDataTrigger>
154                     <MultiDataTrigger>
155                         <MultiDataTrigger.Conditions>
156                             <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineUpButton}" Value="true"/>
157                             <Condition Binding="{Binding IsPressed, ElementName=PART_LineUpButton}" Value="true"/>
158                         </MultiDataTrigger.Conditions>
159                         <Setter Property="Fill" TargetName="ArrowTop" Value="{StaticResource ScrollBar.Pressed.Glyph}"/>
160                     </MultiDataTrigger>
161                     <MultiDataTrigger>
162                         <MultiDataTrigger.Conditions>
163                             <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineDownButton}" Value="true"/>
164                             <Condition Binding="{Binding IsPressed, ElementName=PART_LineDownButton}" Value="false"/>
165                         </MultiDataTrigger.Conditions>
166                         <Setter Property="Fill" TargetName="ArrowBottom" Value="{StaticResource ScrollBar.MouseOver.Glyph}"/>
167                     </MultiDataTrigger>
168                     <MultiDataTrigger>
169                         <MultiDataTrigger.Conditions>
170                             <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineUpButton}" Value="true"/>
171                             <Condition Binding="{Binding IsPressed, ElementName=PART_LineUpButton}" Value="false"/>
172                         </MultiDataTrigger.Conditions>
173                         <Setter Property="Fill" TargetName="ArrowTop" Value="{StaticResource ScrollBar.MouseOver.Glyph}"/>
174                     </MultiDataTrigger>
175                     <Trigger Property="IsEnabled" Value="false">
176                         <Setter Property="Fill" TargetName="ArrowTop" Value="{StaticResource ScrollBar.Disabled.Glyph}"/>
177                         <Setter Property="Fill" TargetName="ArrowBottom" Value="{StaticResource ScrollBar.Disabled.Glyph}"/>
178                     </Trigger>
179                 </ControlTemplate.Triggers>
180             </ControlTemplate>
181         </Setter.Value>
182     </Setter>
183     <Style.Triggers>
184         <Trigger Property="Orientation" Value="Horizontal">
185             <Setter Property="Width" Value="Auto"/>
186             <Setter Property="MinWidth" Value="0"/>
187             <Setter Property="Height" Value="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarHeightKey}}"/>
188             <Setter Property="MinHeight" Value="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarHeightKey}}"/>
189             <Setter Property="BorderThickness" Value="0,1"/>
190             <Setter Property="Template">
191                 <Setter.Value>
192                     <ControlTemplate TargetType="{x:Type ScrollBar}">
193                         <Grid x:Name="Bg" SnapsToDevicePixels="true">
194                             <Grid.ColumnDefinitions>
195                                 <ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}"/>
196                                 <ColumnDefinition Width="0.00001*"/>
197                                 <ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}"/>
198                             </Grid.ColumnDefinitions>
199                             <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="1"/>
200                             <RepeatButton x:Name="PART_LineLeftButton" Command="{x:Static ScrollBar.LineLeftCommand}" IsEnabled="{TemplateBinding IsMouseOver}" Style="{StaticResource ScrollBarButton}">
201                                 <Path x:Name="ArrowLeft" Data="M 3.18,7 C3.18,7 5,7 5,7 5,7 1.81,3.5 1.81,3.5 1.81,3.5 5,0 5,0 5,0 3.18,0 3.18,0 3.18,0 0,3.5 0,3.5 0,3.5 3.18,7 3.18,7 z" Fill="{StaticResource ScrollBar.Static.Glyph}" Margin="3" Stretch="Uniform"/>
202                             </RepeatButton>
203                             <Track x:Name="PART_Track" Grid.Column="1" IsEnabled="{TemplateBinding IsMouseOver}">
204                                 <Track.DecreaseRepeatButton>
205                                     <RepeatButton Command="{x:Static ScrollBar.PageLeftCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
206                                 </Track.DecreaseRepeatButton>
207                                 <Track.IncreaseRepeatButton>
208                                     <RepeatButton Command="{x:Static ScrollBar.PageRightCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
209                                 </Track.IncreaseRepeatButton>
210                                 <Track.Thumb>
211                                     <Thumb Style="{StaticResource ScrollBarThumbHorizontal}"/>
212                                 </Track.Thumb>
213                             </Track>
214                             <RepeatButton x:Name="PART_LineRightButton" Command="{x:Static ScrollBar.LineRightCommand}" Grid.Column="2" IsEnabled="{TemplateBinding IsMouseOver}" Style="{StaticResource ScrollBarButton}">
215                                 <Path x:Name="ArrowRight" Data="M 1.81,7 C1.81,7 0,7 0,7 0,7 3.18,3.5 3.18,3.5 3.18,3.5 0,0 0,0 0,0 1.81,0 1.81,0 1.81,0 5,3.5 5,3.5 5,3.5 1.81,7 1.81,7 z" Fill="{StaticResource ScrollBar.Static.Glyph}" Margin="3" Stretch="Uniform"/>
216                             </RepeatButton>
217                         </Grid>
218                         <ControlTemplate.Triggers>
219                             <MultiDataTrigger>
220                                 <MultiDataTrigger.Conditions>
221                                     <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineRightButton}" Value="true"/>
222                                     <Condition Binding="{Binding IsPressed, ElementName=PART_LineRightButton}" Value="true"/>
223                                 </MultiDataTrigger.Conditions>
224                                 <Setter Property="Fill" TargetName="ArrowRight" Value="{StaticResource ScrollBar.Pressed.Glyph}"/>
225                             </MultiDataTrigger>
226                             <MultiDataTrigger>
227                                 <MultiDataTrigger.Conditions>
228                                     <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineLeftButton}" Value="true"/>
229                                     <Condition Binding="{Binding IsPressed, ElementName=PART_LineLeftButton}" Value="true"/>
230                                 </MultiDataTrigger.Conditions>
231                                 <Setter Property="Fill" TargetName="ArrowLeft" Value="{StaticResource ScrollBar.Pressed.Glyph}"/>
232                             </MultiDataTrigger>
233                             <MultiDataTrigger>
234                                 <MultiDataTrigger.Conditions>
235                                     <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineRightButton}" Value="true"/>
236                                     <Condition Binding="{Binding IsPressed, ElementName=PART_LineRightButton}" Value="false"/>
237                                 </MultiDataTrigger.Conditions>
238                                 <Setter Property="Fill" TargetName="ArrowRight" Value="{StaticResource ScrollBar.MouseOver.Glyph}"/>
239                             </MultiDataTrigger>
240                             <MultiDataTrigger>
241                                 <MultiDataTrigger.Conditions>
242                                     <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineLeftButton}" Value="true"/>
243                                     <Condition Binding="{Binding IsPressed, ElementName=PART_LineLeftButton}" Value="false"/>
244                                 </MultiDataTrigger.Conditions>
245                                 <Setter Property="Fill" TargetName="ArrowLeft" Value="{StaticResource ScrollBar.MouseOver.Glyph}"/>
246                             </MultiDataTrigger>
247                             <Trigger Property="IsEnabled" Value="false">
248                                 <Setter Property="Fill" TargetName="ArrowLeft" Value="{StaticResource ScrollBar.Disabled.Glyph}"/>
249                                 <Setter Property="Fill" TargetName="ArrowRight" Value="{StaticResource ScrollBar.Disabled.Glyph}"/>
250                             </Trigger>
251                         </ControlTemplate.Triggers>
252                     </ControlTemplate>
253                 </Setter.Value>
254             </Setter>
255         </Trigger>
256     </Style.Triggers>
257 </Style>

 

拆解ScrollViewer的样式和模板

ScrollViewer的样式部分没有属性设置,我们直接看它的模板

 1 <ControlTemplate TargetType="{x:Type ScrollViewer}">
 2     <!--使用Grid作为父级容器-->
 3     <Grid x:Name="Grid" Background="{TemplateBinding Background}">
 4         <!--定义两行两列-->
 5         <Grid.ColumnDefinitions>
 6             <ColumnDefinition Width="*"/>
 7             <ColumnDefinition Width="Auto"/>
 8         </Grid.ColumnDefinitions>
 9         <Grid.RowDefinitions>
10             <RowDefinition Height="*"/>
11             <RowDefinition Height="Auto"/>
12         </Grid.RowDefinitions>
13         <!--右下角的区域-->
14         <!--当垂直滚动条和水平滚动条同时显示时,右下角会出现一个矩形空白-->
15         <!--用于显示的一致性-->
16         <Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
17         
18         <!--用于内容显示-->
19         <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanHorizontallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" CanVerticallyScroll="False" Grid.Column="0" Content="{TemplateBinding Content}" CanContentScroll="{TemplateBinding CanContentScroll}" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
20         
21         <!--垂直滚动条-->
22         <ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
23         
24         <!--水平滚动条-->
25         <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
26     </Grid>
27 </ControlTemplate>

 

ScrollViewer的模板使用一个Grid控件作为父级容器,然后定义了两行两列。

它的模板组成结构如下图所示:

image

 

这里我们介绍一下ScrollContentPresenter类型。

ScrollContentPresenter 是 WPF 中 ScrollViewer 控件模板里的一个核心内部组件。

它的主要作用是承载并显示 ScrollViewer 的实际内容,同时负责处理滚动逻辑。可以把它理解为 ScrollViewer 的“内容展示与滚动引擎”。

 

ScrollContentPresenter继承自ContentPresenter类型,同时还实现了IScrollInfo接口

ContentPresenter用于内容显示,IScrollInfo接口用于支持滚动。

1 public sealed class ScrollContentPresenter : System.Windows.Controls.ContentPresenter, System.Windows.Controls.Primitives.IScrollInfo

 

关于ContentPresenter类型在前面的文章中我们进行过详细介绍

https://www.cnblogs.com/zhaotianff/p/22706548

 

下面我们介绍一下IScrollInfo接口

IScrollInfo接口表示 ScrollViewer 控件内部的主要可滚动区域。

IScrollInfo 接口提供了对启用子内容的逻辑滚动的方法的访问,以及计算哪些子元素可见。

 

 

此外,它还定义用于定义可滚动区域的属性。例如:

  • CanHorizontallyScroll / CanVerticallyScroll:这两个属性分别指示是否允许在水平/垂直方向上进行滚动。

  • HorizontalOffset / VerticalOffset:获取或设置内容在水平/垂直方向上的滚动偏移量。

  • ExtentHeight / ExtentWidth:获取内容的总高度/宽度。

  • ViewportHeight / ViewportWidth:获取当前可视区域的高度/宽度。

  • ScrollOwner:获取或设置控制此 ScrollContentPresenter 滚动行为的 ScrollViewer 元素。

 

IScrollInfo接口提供了一系列控制滚动的方法,例如:

  • LineUp(), LineDown(), LineLeft(), LineRight():按行滚动。

  • PageUp(), PageDown(), PageLeft(), PageRight():按页滚动。

  • MouseWheelUp(), MouseWheelDown():响应鼠标滚轮。

  • SetHorizontalOffset(double), SetVerticalOffset(double):直接设置滚动偏移量。

  • MakeVisible(Visual, Rect):尝试将指定元素滚动到可视区域内。

 

总结来说:ScrollViewer 通过模板中的水平/垂直 ScrollBar 接收用户的滚动输入,然后由 ScrollViewer 把滚动请求转发给实现了 IScrollInfo 的滚动逻辑组件(ScrollContentPresenter),从而实现滚动。此外,ScrollBar 还负责显示当前滚动状态。

 

所以在我们自定义模板时,所有元素的名称是要保持跟系统定义的一致:

1、必须包含一个类型为 ScrollContentPresenter 且 Name 为 PART_ScrollContentPresenter 的元素。

2、必须包含两个类型为 ScrollBar 且 Name分别为PART_VerticalScrollBar 和 PART_HorizontalScrollBar的元素。

注意:如果元素名称不一致,滚动功能就会异常。

1  <!--用于内容显示-->
2  <ScrollContentPresenter x:Name="PART_ScrollContentPresenter"/>
3  
4  <!--垂直滚动条-->
5  <ScrollBar x:Name="PART_VerticalScrollBar"/>
6  
7  <!--水平滚动条-->
8  <ScrollBar x:Name="PART_HorizontalScrollBar"/>

 

它的内部原理如下:

ScrollViewer 在应用模板时,会在可视化树中查找名为 PART_ScrollContentPresenter 的元素,并将其与自身关联。

然后,ScrollViewer 的滚动条(PART_VerticalScrollBar 和 PART_HorizontalScrollBar)会与这个 ScrollContentPresenter 的 IScrollInfo 属性进行绑定,实现同步。

 

拆解ScrollBar的样式和模板

 1、首先是Tab 键切换控件焦点时看到的虚线焦点框样式,如下所示

1 <Style x:Key="FocusVisual">
2     <Setter Property="Control.Template">
3         <Setter.Value>
4             <ControlTemplate>
5                 <Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
6             </ControlTemplate>
7         </Setter.Value>
8     </Setter>
9 </Style>

 

2、资源画刷定义

 1 <SolidColorBrush x:Key="ScrollBar.Static.Background" Color="#F0F0F0"/>
 2 <SolidColorBrush x:Key="ScrollBar.Static.Border" Color="#F0F0F0"/>
 3 <SolidColorBrush x:Key="ScrollBar.Static.Glyph" Color="#606060"/>
 4 <SolidColorBrush x:Key="ScrollBar.Static.Thumb" Color="#CDCDCD"/>
 5 <SolidColorBrush x:Key="ScrollBar.MouseOver.Background" Color="#DADADA"/>
 6 <SolidColorBrush x:Key="ScrollBar.MouseOver.Border" Color="#DADADA"/>
 7 <SolidColorBrush x:Key="ScrollBar.MouseOver.Glyph" Color="#000000"/>
 8 <SolidColorBrush x:Key="ScrollBar.MouseOver.Thumb" Color="#A6A6A6"/>
 9 <SolidColorBrush x:Key="ScrollBar.Pressed.Background" Color="#606060"/>
10 <SolidColorBrush x:Key="ScrollBar.Pressed.Border" Color="#606060"/>
11 <SolidColorBrush x:Key="ScrollBar.Pressed.Thumb" Color="#606060"/>
12 <SolidColorBrush x:Key="ScrollBar.Pressed.Glyph" Color="#FFFFFF"/>
13 <SolidColorBrush x:Key="ScrollBar.Disabled.Background" Color="#F0F0F0"/>
14 <SolidColorBrush x:Key="ScrollBar.Disabled.Border" Color="#F0F0F0"/>
15 <SolidColorBrush x:Key="ScrollBar.Disabled.Glyph" Color="#BFBFBF"/>

 

3、滚动条透明区域样式

 1 <Style x:Key="RepeatButtonTransparent" TargetType="{x:Type RepeatButton}">
 2     <Setter Property="OverridesDefaultStyle" Value="true"/>
 3     <Setter Property="Background" Value="Transparent"/>
 4     <Setter Property="Focusable" Value="false"/>
 5     <Setter Property="IsTabStop" Value="false"/>
 6     <Setter Property="Template">
 7         <Setter.Value>
 8             <ControlTemplate TargetType="{x:Type RepeatButton}">
 9                 <Rectangle Fill="{TemplateBinding Background}" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}"/>
10             </ControlTemplate>
11         </Setter.Value>
12     </Setter>
13 </Style>

 

先用一张图展示一下这个样式具体是对应到哪个控件。(方便演示,使用了红色和绿色来区分)

 

image

 

正常一个滚动条区域,会由一个滑块和滑轨组成,滑块上面的区域是一个RepeatButton,滑块下面的区域也是一个RepeatButton。

这两个RepeatButton使用的样式就是上面提到的滚动条透明区域样式

防止有些小伙伴不熟悉RepeatButton,这里把RepeatButton拿出来快速讲解一下。

RepeatButton也是一个按钮控件,它类似于Button。但是,RepeatButton 元素可让你控制 Click 事件发生的时间和方式。

通俗点来说就是:Button 是普通单击按钮,点一次触发一次 Click;RepeatButton 是“按住会重复触发”的按钮,按住期间会不断触发 Click/Command,直到松开。

RepeatButton 有两个关键属性:

  • Delay:按下后等待多久开始重复,单位毫秒,默认通常为 500ms

  • Interval:开始重复后,每次触发之间的间隔,单位毫秒,默认通常为 33ms

 

ScrollBar这里之所以使用RepeatButton,是因为滚动条空白区域是可以通过按住不放来实现持续滚动的。

demo

 

4、两个箭头按钮样式

 1 <!--ScrollBar的两个箭头按钮样式-->
 2 <Style x:Key="ScrollBarButton" TargetType="{x:Type RepeatButton}">
 3     <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
 4     <Setter Property="BorderThickness" Value="1"/>
 5     <Setter Property="HorizontalContentAlignment" Value="Center"/>
 6     <Setter Property="VerticalContentAlignment" Value="Center"/>
 7     <Setter Property="Padding" Value="1"/>
 8     <Setter Property="Focusable" Value="false"/>
 9     <Setter Property="IsTabStop" Value="false"/>
10     <Setter Property="Template">
11         <Setter.Value>
12             <ControlTemplate TargetType="{x:Type RepeatButton}">
13                 <Border x:Name="border" Background="{StaticResource ScrollBar.Static.Background}" BorderBrush="{StaticResource ScrollBar.Static.Border}" BorderThickness="1" SnapsToDevicePixels="true">
14                     <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
15                 </Border>
16                 <ControlTemplate.Triggers>
17                     <Trigger Property="IsMouseOver" Value="true">
18                         <Setter Property="Background" TargetName="border" Value="{StaticResource ScrollBar.MouseOver.Background}"/>
19                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource ScrollBar.MouseOver.Border}"/>
20                     </Trigger>
21                     <Trigger Property="IsPressed" Value="true">
22                         <Setter Property="Background" TargetName="border" Value="{StaticResource ScrollBar.Pressed.Background}"/>
23                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource ScrollBar.Pressed.Border}"/>
24                     </Trigger>
25                     <Trigger Property="IsEnabled" Value="false">
26                         <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
27                         <Setter Property="Background" TargetName="border" Value="{StaticResource ScrollBar.Disabled.Background}"/>
28                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource ScrollBar.Disabled.Border}"/>
29                     </Trigger>
30                 </ControlTemplate.Triggers>
31             </ControlTemplate>
32         </Setter.Value>
33     </Setter>
34 </Style>

 

通过下面的示意图可以看到,这个RepeatButton样式,对应的是顶部/底部的两个小箭头按钮样式。

需要注意的是:这里暂时只设置了样式,并没有设置按钮的内容。按钮的内容会在接下来的样式讲解中说明。

image

 

5、滑块的样式

垂直滑块的样式

 1 <!--垂直滑块的样式-->
 2 <Style x:Key="ScrollBarThumbVertical" TargetType="{x:Type Thumb}">
 3     <Setter Property="OverridesDefaultStyle" Value="true"/>
 4     <Setter Property="IsTabStop" Value="false"/>
 5     <Setter Property="Template">
 6         <Setter.Value>
 7             <ControlTemplate TargetType="{x:Type Thumb}">
 8                 <!--通过Rectangle来实现一个滑块-->
 9                 <Rectangle x:Name="rectangle" Fill="{StaticResource ScrollBar.Static.Thumb}" Height="{TemplateBinding Height}" SnapsToDevicePixels="True" Width="{TemplateBinding Width}"/>
10                 <ControlTemplate.Triggers>
11                     <!--鼠标悬停时-->
12                     <Trigger Property="IsMouseOver" Value="true">
13                         <Setter Property="Fill" TargetName="rectangle" Value="{StaticResource ScrollBar.MouseOver.Thumb}"/>
14                     </Trigger>
15                     <!--拖动时-->
16                     <Trigger Property="IsDragging" Value="true">
17                         <Setter Property="Fill" TargetName="rectangle" Value="{StaticResource ScrollBar.Pressed.Thumb}"/>
18                     </Trigger>
19                 </ControlTemplate.Triggers>
20             </ControlTemplate>
21         </Setter.Value>
22     </Setter>
23 </Style>

 

水平滑块的样式

 1  <!--水平滑块的样式-->
 2  <Style x:Key="ScrollBarThumbHorizontal" TargetType="{x:Type Thumb}">
 3      <Setter Property="OverridesDefaultStyle" Value="true"/>
 4      <Setter Property="IsTabStop" Value="false"/>
 5      <Setter Property="Template">
 6          <Setter.Value>
 7              <ControlTemplate TargetType="{x:Type Thumb}">
 8                  <!--通过Rectangle来实现一个滑块-->
 9                  <Rectangle x:Name="rectangle" Fill="{StaticResource ScrollBar.Static.Thumb}" Height="{TemplateBinding Height}" SnapsToDevicePixels="True" Width="{TemplateBinding Width}"/>
10                  <ControlTemplate.Triggers>
11                      <!--鼠标悬停时-->
12                      <Trigger Property="IsMouseOver" Value="true">
13                          <Setter Property="Fill" TargetName="rectangle" Value="{StaticResource ScrollBar.MouseOver.Thumb}"/>
14                      </Trigger>
15                      <!--拖动时-->
16                      <Trigger Property="IsDragging" Value="true">
17                          <Setter Property="Fill" TargetName="rectangle" Value="{StaticResource ScrollBar.Pressed.Thumb}"/>
18                      </Trigger>
19                  </ControlTemplate.Triggers>
20              </ControlTemplate>
21          </Setter.Value>
22      </Setter>
23  </Style>

 

滑块示意如下

image

这里是通过两个Rectangle来实现,并通过触发器来实现鼠标悬停和拖动时的效果。

我们可以尝试把滑块改成一个椭圆

1 <Ellipse x:Name="rectangle" Fill="{StaticResource ScrollBar.Static.Thumb}" SnapsToDevicePixels="True" Width="{TemplateBinding Width}" Height="{TemplateBinding ActualWidth}"/>

效果如下:

image

 

6、Track控件

在介绍ScrollBar样式前,我们还需要了解一下Track这个控件。

因为Track控件是ScrollBar控件的组成部分之一。

Track控件是一个基元控件(control primitive),该控件负责处理Thumb控件与两个RepeatButton控件的位置摆放和尺寸大小,而这几个控件用于设置一个Value

可以通过下面的图来直观感受一下

0374443220f22b43aeb144411df0ee95_scrollbargeneric_view=windowsdesktop-10.0

这里所说的Control Primitive,是指这个控件一般并不会在业务界面使用,仅在控件内部使用,例如Slider控件和ScrollBar控件。

Track控件没有默认的控件模板,它的控件模板是通过ScrollBar和Slider控件模板来实现的。

Track由Thumb控件和两个RepeatButton组成,所以我们可以单独设置Thumb和RepeatButton的样式。

 1 <Track x:Name="PART_Track" IsDirectionReversed="true" IsEnabled="{TemplateBinding IsMouseOver}" Grid.Row="1">
 2     <!--DecreaseRepeatButton是Track的一个属性-->
 3     <!--设置用于减小 Track 控件 Value 属性的 RepeatButton-->
 4     <Track.DecreaseRepeatButton>
 5         <RepeatButton Command="{x:Static ScrollBar.PageUpCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
 6     </Track.DecreaseRepeatButton>
 7     <!--IncreaseRepeatButton是Track的一个属性-->
 8     <!--设置用于增加 Track 控件 Value 属性的 RepeatButton-->
 9     <Track.IncreaseRepeatButton>
10         <RepeatButton Command="{x:Static ScrollBar.PageDownCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
11     </Track.IncreaseRepeatButton>
12     <!--Thumb是Track的一个属性-->
13     <!--设置用于更改 Track 控件 Value 属性的 Thumb-->
14     <Track.Thumb>
15         <Thumb Style="{StaticResource ScrollBarThumbVertical}"/>
16     </Track.Thumb>
17 </Track>

 

此外,我们还需要了解一下Track.Orientation这个依赖属性,这个属性可以获取或设置Track是水平显示还是垂直显示。

ScrollBar的Orientation属性就是绑定到了Track.Orientation属性。

我们在设置ScrollBar的水平或垂直滚动条样式时就需要用到这个属性。

 1 <Style x:Key="ScrollBarStyle1" TargetType="{x:Type ScrollBar}">
 2     <!--默认是设置垂直状态ScrollBar样式-->
 3     <Setter Property="Template">
 4         <Setter.Value>
 5             <ControlTemplate TargetType="{x:Type ScrollBar}">
 6                 <Grid x:Name="Bg" SnapsToDevicePixels="true">
 7                      ......
 8                 </Grid>
 9             </ControlTemplate>
10         </Setter.Value>
11     </Setter>
12     <Style.Triggers>
13         <!--通过触发器设置水平状态-->
14         <Trigger Property="Orientation" Value="Horizontal">
15             <Setter Property="Template">
16                 <Setter.Value>
17                     <ControlTemplate TargetType="{x:Type ScrollBar}">
18                         <Grid x:Name="Bg" SnapsToDevicePixels="true">
19                               ......
20                         </Grid>
21                     </ControlTemplate>
22                 </Setter.Value>
23             </Setter>
24         </Trigger>
25     </Style.Triggers>
26 </Style>

 

更详细的Track控件介绍可以参考官网的文档链接

https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.track?view=windowsdesktop-10.0

 

7、垂直ScrollBar样式和模板

前面已经把ScrollBar子元素的样式介绍完了,现在正式进入ScrollBar样式的讲解。

ScrollBar样式分为水平和垂直两种样式。

我们先介绍一下ScrollBar的垂直样式和模板。

 1 <Style x:Key="ScrollBarStyle1" TargetType="{x:Type ScrollBar}">
 2     <Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
 3     <Setter Property="Stylus.IsFlicksEnabled" Value="false"/>
 4     <Setter Property="Background" Value="{StaticResource ScrollBar.Static.Background}"/>
 5     <Setter Property="BorderBrush" Value="{StaticResource ScrollBar.Static.Border}"/>
 6     <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
 7     <Setter Property="BorderThickness" Value="1,0"/>
 8     <Setter Property="Width" Value="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"/>
 9     <Setter Property="MinWidth" Value="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"/>
10     <Setter Property="Template">
11         <Setter.Value>
12             <ControlTemplate TargetType="{x:Type ScrollBar}">
13                 <!--使用Grid定义三行-->
14                 <Grid x:Name="Bg" SnapsToDevicePixels="true">
15                     <Grid.RowDefinitions>
16                         <!--第一行用于显示鼠标按住时向上滚动的RepeatButton-->
17                         <RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}"/>
18                         <!--第二行用于显示滑块区域-->
19                         <RowDefinition Height="0.00001*"/>
20                         <!--第三行用于显示鼠标按住时向下滚动的RepeatButton-->
21                         <RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}"/>
22                     </Grid.RowDefinitions>
23                     <!--滚动区域背景-->
24                     <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.Row="1"/>
25                     <RepeatButton x:Name="PART_LineUpButton" Command="{x:Static ScrollBar.LineUpCommand}" IsEnabled="{TemplateBinding IsMouseOver}" Style="{StaticResource ScrollBarButton}">
26                         <!--向上的箭头图形-->
27                         <Path x:Name="ArrowTop" Data="M 0,4 C0,4 0,6 0,6 0,6 3.5,2.5 3.5,2.5 3.5,2.5 7,6 7,6 7,6 7,4 7,4 7,4 3.5,0.5 3.5,0.5 3.5,0.5 0,4 0,4 z" Fill="{StaticResource ScrollBar.Static.Glyph}" Margin="3,4,3,3" Stretch="Uniform"/>
28                     </RepeatButton>
29                     <Track x:Name="PART_Track" IsDirectionReversed="true" IsEnabled="{TemplateBinding IsMouseOver}" Grid.Row="1">
30                         <!--DecreaseRepeatButton是Track的一个属性-->
31                         <!--设置用于减小 Track 控件 Value 属性的 RepeatButton-->
32                         <Track.DecreaseRepeatButton>
33                             <RepeatButton Command="{x:Static ScrollBar.PageUpCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
34                         </Track.DecreaseRepeatButton>
35                         <!--IncreaseRepeatButton是Track的一个属性-->
36                         <!--设置用于增加 Track 控件 Value 属性的 RepeatButton-->
37                         <Track.IncreaseRepeatButton>
38                             <RepeatButton Command="{x:Static ScrollBar.PageDownCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
39                         </Track.IncreaseRepeatButton>
40                         <!--Thumb是Track的一个属性-->
41                         <!--设置用于更改 Track 控件 Value 属性的 Thumb-->
42                         <Track.Thumb>
43                             <Thumb Style="{StaticResource ScrollBarThumbVertical}"/>
44                         </Track.Thumb>
45                     </Track>
46                     <RepeatButton x:Name="PART_LineDownButton" Command="{x:Static ScrollBar.LineDownCommand}" IsEnabled="{TemplateBinding IsMouseOver}" Grid.Row="2" Style="{StaticResource ScrollBarButton}">
47                         <!--向下的箭头图形-->
48                         <Path x:Name="ArrowBottom" Data="M 0,2.5 C0,2.5 0,0.5 0,0.5 0,0.5 3.5,4 3.5,4 3.5,4 7,0.5 7,0.5 7,0.5 7,2.5 7,2.5 7,2.5 3.5,6 3.5,6 3.5,6 0,2.5 0,2.5 z" Fill="{StaticResource ScrollBar.Static.Glyph}" Margin="3,4,3,3" Stretch="Uniform"/>
49                     </RepeatButton>
50                 </Grid>
51          </ControlTemplate >
52       </Setter.Value>
53    </Setter>
54 </Style>

 

关于属性设置这部分,这里不做详细介绍,基本上都是控件常见的一些属性。

模板部分,这里是通过Grid定义了三行,第一行显示向上的箭头按钮,第二行显示滚动区域及滚动滑块,第三行显示向下的箭头按钮。

滚动区域是通过一个Border来实现,滚动功能是通过Track控件来实现,在前面已经介绍过了。

 

接下来是模板的触发器部分,这里主要是控件向上/向下箭头的一些显示效果,可以就不做详细介绍了。

自己在设计样式时,直接参考下面的模式,设置不同的配色即可。

 1 <ControlTemplate.Triggers>
 2     <MultiDataTrigger>
 3         <MultiDataTrigger.Conditions>
 4             <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineDownButton}" Value="true"/>
 5             <Condition Binding="{Binding IsPressed, ElementName=PART_LineDownButton}" Value="true"/>
 6         </MultiDataTrigger.Conditions>
 7         <Setter Property="Fill" TargetName="ArrowBottom" Value="{StaticResource ScrollBar.Pressed.Glyph}"/>
 8     </MultiDataTrigger>
 9     <MultiDataTrigger>
10         <MultiDataTrigger.Conditions>
11             <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineUpButton}" Value="true"/>
12             <Condition Binding="{Binding IsPressed, ElementName=PART_LineUpButton}" Value="true"/>
13         </MultiDataTrigger.Conditions>
14         <Setter Property="Fill" TargetName="ArrowTop" Value="{StaticResource ScrollBar.Pressed.Glyph}"/>
15     </MultiDataTrigger>
16     <MultiDataTrigger>
17         <MultiDataTrigger.Conditions>
18             <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineDownButton}" Value="true"/>
19             <Condition Binding="{Binding IsPressed, ElementName=PART_LineDownButton}" Value="false"/>
20         </MultiDataTrigger.Conditions>
21         <Setter Property="Fill" TargetName="ArrowBottom" Value="{StaticResource ScrollBar.MouseOver.Glyph}"/>
22     </MultiDataTrigger>
23     <MultiDataTrigger>
24         <MultiDataTrigger.Conditions>
25             <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineUpButton}" Value="true"/>
26             <Condition Binding="{Binding IsPressed, ElementName=PART_LineUpButton}" Value="false"/>
27         </MultiDataTrigger.Conditions>
28         <Setter Property="Fill" TargetName="ArrowTop" Value="{StaticResource ScrollBar.MouseOver.Glyph}"/>
29     </MultiDataTrigger>
30     <Trigger Property="IsEnabled" Value="false">
31         <Setter Property="Fill" TargetName="ArrowTop" Value="{StaticResource ScrollBar.Disabled.Glyph}"/>
32         <Setter Property="Fill" TargetName="ArrowBottom" Value="{StaticResource ScrollBar.Disabled.Glyph}"/>
33     </Trigger>
34 </ControlTemplate.Triggers>

 

接下来我们可以看到ScrollBar的水平样式和模板,ScrollBar的水平样式切换是通过Orientation属性来触发,这个属性也在前面介绍过了。

 1 <Style.Triggers>
 2     <!--水平ScrollBar-->
 3     <Trigger Property="Orientation" Value="Horizontal">
 4         <Setter Property="Width" Value="Auto"/>
 5         <Setter Property="MinWidth" Value="0"/>
 6         <Setter Property="Height" Value="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarHeightKey}}"/>
 7         <Setter Property="MinHeight" Value="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarHeightKey}}"/>
 8         <Setter Property="BorderThickness" Value="0,1"/>
 9         <Setter Property="Template">
10             <Setter.Value>
11                 <ControlTemplate TargetType="{x:Type ScrollBar}">
12                     <!--使用Grid定义三列-->
13                     <Grid x:Name="Bg" SnapsToDevicePixels="true">
14                         <Grid.ColumnDefinitions>
15                             <!--第一列用于显示鼠标按住时向左滚动的RepeatButton-->
16                             <ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}"/>
17                             <!--第二列用于显示滚动区域-->
18                             <ColumnDefinition Width="0.00001*"/>
19                             <!--第三列用于显示鼠标按住时向右滚动的RepeatButton-->
20                             <ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}"/>
21                         </Grid.ColumnDefinitions>
22                         <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="1"/>
23                         <RepeatButton x:Name="PART_LineLeftButton" Command="{x:Static ScrollBar.LineLeftCommand}" IsEnabled="{TemplateBinding IsMouseOver}" Style="{StaticResource ScrollBarButton}">
24                             <Path x:Name="ArrowLeft" Data="M 3.18,7 C3.18,7 5,7 5,7 5,7 1.81,3.5 1.81,3.5 1.81,3.5 5,0 5,0 5,0 3.18,0 3.18,0 3.18,0 0,3.5 0,3.5 0,3.5 3.18,7 3.18,7 z" Fill="{StaticResource ScrollBar.Static.Glyph}" Margin="3" Stretch="Uniform"/>
25                         </RepeatButton>
26                         <Track x:Name="PART_Track" Grid.Column="1" IsEnabled="{TemplateBinding IsMouseOver}">
27                             <Track.DecreaseRepeatButton>
28                                 <RepeatButton Command="{x:Static ScrollBar.PageLeftCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
29                             </Track.DecreaseRepeatButton>
30                             <Track.IncreaseRepeatButton>
31                                 <RepeatButton Command="{x:Static ScrollBar.PageRightCommand}" Style="{StaticResource RepeatButtonTransparent}"/>
32                             </Track.IncreaseRepeatButton>
33                             <Track.Thumb>
34                                 <Thumb Style="{StaticResource ScrollBarThumbHorizontal}"/>
35                             </Track.Thumb>
36                         </Track>
37                         <RepeatButton x:Name="PART_LineRightButton" Command="{x:Static ScrollBar.LineRightCommand}" Grid.Column="2" IsEnabled="{TemplateBinding IsMouseOver}" Style="{StaticResource ScrollBarButton}">
38                             <Path x:Name="ArrowRight" Data="M 1.81,7 C1.81,7 0,7 0,7 0,7 3.18,3.5 3.18,3.5 3.18,3.5 0,0 0,0 0,0 1.81,0 1.81,0 1.81,0 5,3.5 5,3.5 5,3.5 1.81,7 1.81,7 z" Fill="{StaticResource ScrollBar.Static.Glyph}" Margin="3" Stretch="Uniform"/>
39                         </RepeatButton>
40                     </Grid>
41                     <ControlTemplate.Triggers>
42                         <MultiDataTrigger>
43                             <MultiDataTrigger.Conditions>
44                                 <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineRightButton}" Value="true"/>
45                                 <Condition Binding="{Binding IsPressed, ElementName=PART_LineRightButton}" Value="true"/>
46                             </MultiDataTrigger.Conditions>
47                             <Setter Property="Fill" TargetName="ArrowRight" Value="{StaticResource ScrollBar.Pressed.Glyph}"/>
48                         </MultiDataTrigger>
49                         <MultiDataTrigger>
50                             <MultiDataTrigger.Conditions>
51                                 <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineLeftButton}" Value="true"/>
52                                 <Condition Binding="{Binding IsPressed, ElementName=PART_LineLeftButton}" Value="true"/>
53                             </MultiDataTrigger.Conditions>
54                             <Setter Property="Fill" TargetName="ArrowLeft" Value="{StaticResource ScrollBar.Pressed.Glyph}"/>
55                         </MultiDataTrigger>
56                         <MultiDataTrigger>
57                             <MultiDataTrigger.Conditions>
58                                 <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineRightButton}" Value="true"/>
59                                 <Condition Binding="{Binding IsPressed, ElementName=PART_LineRightButton}" Value="false"/>
60                             </MultiDataTrigger.Conditions>
61                             <Setter Property="Fill" TargetName="ArrowRight" Value="{StaticResource ScrollBar.MouseOver.Glyph}"/>
62                         </MultiDataTrigger>
63                         <MultiDataTrigger>
64                             <MultiDataTrigger.Conditions>
65                                 <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineLeftButton}" Value="true"/>
66                                 <Condition Binding="{Binding IsPressed, ElementName=PART_LineLeftButton}" Value="false"/>
67                             </MultiDataTrigger.Conditions>
68                             <Setter Property="Fill" TargetName="ArrowLeft" Value="{StaticResource ScrollBar.MouseOver.Glyph}"/>
69                         </MultiDataTrigger>
70                         <Trigger Property="IsEnabled" Value="false">
71                             <Setter Property="Fill" TargetName="ArrowLeft" Value="{StaticResource ScrollBar.Disabled.Glyph}"/>
72                             <Setter Property="Fill" TargetName="ArrowRight" Value="{StaticResource ScrollBar.Disabled.Glyph}"/>
73                         </Trigger>
74                     </ControlTemplate.Triggers>
75                 </ControlTemplate>
76             </Setter.Value>
77         </Setter>
78     </Trigger>
79 </Style.Triggers>

ScrollBar的水平样式只是将Grid的定义由行转换为列,其它逻辑基本上是一样的,这里就不再赘叙了。

 

VisualState

VisualState 名称VisualStateGroup 名称Description
InvalidFocused ValidationStates Validation.HasError附加属性是true,且控件具有焦点。
InvalidUnfocused ValidationStates Validation.HasError附加属性是true,且控件没有焦点。
Valid ValidationStates 该控件使用 Validation 类, Validation.HasError 附加属性为 false.

 

这几个VisualState对于我们自定义样式来说,基本上用处不大,所以这里就不做详细介绍了。

因为这几个VisualState都是跟验证相关的,正常来说,ScrollViewer也不会去做这样一个操作。

 

ScrollViewer样式示例

我们先定义滚动条透明区域样式

 1     <!--滚动条透明区域样式-->
 2     <Style x:Key="WD.RepeatButtonTransparent" TargetType="{x:Type RepeatButton}">
 3         <Setter Property="OverridesDefaultStyle" Value="True" />
 4         <Setter Property="Background" Value="Transparent" />
 5         <Setter Property="Focusable" Value="False" />
 6         <Setter Property="IsTabStop" Value="False" />
 7         <Setter Property="SnapsToDevicePixels" Value="True" />
 8         <Setter Property="UseLayoutRounding" Value="True" />
 9         <Setter Property="Template">
10             <Setter.Value>
11                 <ControlTemplate TargetType="{x:Type RepeatButton}">
12                     <Rectangle Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Fill="{TemplateBinding Background}" />
13                 </ControlTemplate>
14             </Setter.Value>
15         </Setter>
16     </Style>

 

然后再定义垂直/水平滚动滑块样式

这里主要是控制好Rectangle的显示效果即可

 1  <!--垂直滚动滑块样式-->
 2  <Style x:Key="WD.ScrollBarThumbVertical"  TargetType="{x:Type Thumb}">
 3      <Setter Property="OverridesDefaultStyle" Value="True" />
 4      <Setter Property="Background" Value="Transparent" />
 5      <Setter Property="Focusable" Value="False" />
 6      <Setter Property="IsTabStop" Value="False" />
 7      <Setter Property="SnapsToDevicePixels" Value="True" />
 8      <Setter Property="UseLayoutRounding" Value="True" />
 9      <Setter Property="Template">
10          <Setter.Value>
11              <ControlTemplate TargetType="{x:Type Thumb}">
12                  <Border x:Name="PART_Thumbborder">
13                      <Rectangle x:Name="PART_Rectangle" Height="{TemplateBinding Height}" Fill="{DynamicResource WD.RegularTextBrush}" RadiusX="1" RadiusY="1" />
14                  </Border>
15              </ControlTemplate>
16          </Setter.Value>
17      </Setter>
18  </Style>
19  
20  <!--水平滚动滑块样式-->
21  <Style x:Key="WD.ScrollBarThumbHorizontal" TargetType="{x:Type Thumb}">
22      <Setter Property="OverridesDefaultStyle" Value="True" />
23      <Setter Property="Background" Value="Transparent" />
24      <Setter Property="Focusable" Value="False" />
25      <Setter Property="IsTabStop" Value="False" />
26      <Setter Property="SnapsToDevicePixels" Value="True" />
27      <Setter Property="UseLayoutRounding" Value="True" />
28      <Setter Property="Template">
29          <Setter.Value>
30              <ControlTemplate TargetType="{x:Type Thumb}">
31                  <Border x:Name="PART_Thumbborder">
32                      <Rectangle x:Name="PART_Rectangle" Height="{TemplateBinding Height}" Fill="{DynamicResource WD.PrimaryTextBrush}" RadiusX="1" RadiusY="1" />
33                  </Border>
34              </ControlTemplate>
35          </Setter.Value>
36      </Setter>
37  </Style>

 

然后再定义ScrollBar样式,包括水平和垂直的效果

 1 <Style x:Key="WD.DefaultScrollBar" TargetType="{x:Type ScrollBar}">
 2     <!--为滑块增加动画效果,当鼠标进入时,滑块变宽-->
 3     <Style.Resources>
 4         <Storyboard x:Key="ScrollMouseOut">
 5             <DoubleAnimation Storyboard.TargetName="PART_Track" Storyboard.TargetProperty="Width" To="2" Duration="00:00:0.1" />
 6         </Storyboard>
 7         <Storyboard x:Key="ScrollMouseOver">
 8             <DoubleAnimation Storyboard.TargetName="PART_Track" Storyboard.TargetProperty="Width" To="10" Duration="00:00:0.1" />
 9         </Storyboard>
10     </Style.Resources>
11     <Setter Property="Stylus.IsPressAndHoldEnabled" Value="False" />
12     <Setter Property="Stylus.IsFlicksEnabled" Value="False" />
13     <Setter Property="BorderThickness" Value="0" />
14     <Setter Property="Width" Value="12" />
15     <Setter Property="MinWidth" Value="8" />
16     <Setter Property="SnapsToDevicePixels" Value="True" />
17     <Setter Property="UseLayoutRounding" Value="True" />
18     <Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />
19     <Setter Property="Template">
20         <Setter.Value>
21             <ControlTemplate TargetType="{x:Type ScrollBar}">
22                 <!--这里省略了向上/向下箭头按钮-->
23                 <!--只保留了中间的滚动区域-->
24                 <!--使用Grid作为容器-->
25                 <Grid x:Name="Bg" Width="{TemplateBinding Width}" Background="Transparent" SnapsToDevicePixels="True">
26                     <Track x:Name="PART_Track" Width="2" Margin="0,0,1,0" HorizontalAlignment="Right" IsDirectionReversed="True" IsEnabled="{TemplateBinding IsMouseOver}">
27                         <Track.DecreaseRepeatButton>
28                             <RepeatButton Command="{x:Static ScrollBar.PageUpCommand}" Style="{StaticResource WD.RepeatButtonTransparent}" />
29                         </Track.DecreaseRepeatButton>
30                         <Track.IncreaseRepeatButton>
31                             <RepeatButton Command="{x:Static ScrollBar.PageDownCommand}" Style="{StaticResource WD.RepeatButtonTransparent}" />
32                         </Track.IncreaseRepeatButton>
33                         <Track.Thumb>
34                             <Thumb Style="{StaticResource WD.ScrollBarThumbVertical}" />
35                         </Track.Thumb>
36                     </Track>
37                 </Grid>
38                 <!--鼠标划过时,触发滚动条变宽动画-->
39                 <ControlTemplate.Triggers>
40                     <Trigger Property="IsMouseOver" Value="True">
41                         <Trigger.ExitActions>
42                             <BeginStoryboard Storyboard="{StaticResource ScrollMouseOut}" />
43                         </Trigger.ExitActions>
44                         <Trigger.EnterActions>
45                             <BeginStoryboard Storyboard="{StaticResource ScrollMouseOver}" />
46                         </Trigger.EnterActions>
47                     </Trigger>
48                 </ControlTemplate.Triggers>
49             </ControlTemplate>
50         </Setter.Value>
51     </Setter>
52     <Style.Triggers>
53         <Trigger Property="Orientation" Value="Horizontal">
54             <Setter Property="Width" Value="Auto" />
55             <Setter Property="MinWidth" Value="0" />
56             <Setter Property="Height" Value="12" />
57             <Setter Property="MinHeight" Value="8" />
58             <Setter Property="Template">
59                 <Setter.Value>
60                     <ControlTemplate TargetType="{x:Type ScrollBar}">
61                         <!--为滑块增加动画效果,当鼠标进入时,滑块变高-->
62                         <ControlTemplate.Resources>
63                             <Storyboard x:Key="ScrollMouseOutHeight">
64                                 <DoubleAnimation Storyboard.TargetName="PART_Track" Storyboard.TargetProperty="Height" To="2" Duration="00:00:0.1" />
65                             </Storyboard>
66                             <Storyboard x:Key="ScrollMouseOverHeight">
67                                 <DoubleAnimation Storyboard.TargetName="PART_Track" Storyboard.TargetProperty="Height" To="10" Duration="00:00:0.1" />
68                             </Storyboard>
69                         </ControlTemplate.Resources>
70                         <Grid Height="{TemplateBinding Height}" Background="Transparent" SnapsToDevicePixels="True">
71                             <Track x:Name="PART_Track" Height="2" Margin="0,0,0,1" VerticalAlignment="Bottom" IsEnabled="{TemplateBinding IsMouseOver}" Visibility="Hidden">
72                                 <Track.DecreaseRepeatButton>
73                                     <RepeatButton Command="{x:Static ScrollBar.PageLeftCommand}" Style="{StaticResource WD.RepeatButtonTransparent}" />
74                                 </Track.DecreaseRepeatButton>
75                                 <Track.IncreaseRepeatButton>
76                                     <RepeatButton Command="{x:Static ScrollBar.PageRightCommand}" Style="{StaticResource WD.RepeatButtonTransparent}" />
77                                 </Track.IncreaseRepeatButton>
78                                 <Track.Thumb>
79                                     <Thumb Style="{StaticResource WD.ScrollBarThumbHorizontal}" />
80                                 </Track.Thumb>
81                             </Track>
82                         </Grid>
83                         <ControlTemplate.Triggers>
84                             <Trigger Property="IsMouseOver" Value="True">
85                                 <Trigger.ExitActions>
86                                     <BeginStoryboard Storyboard="{StaticResource ScrollMouseOutHeight}" />
87                                 </Trigger.ExitActions>
88                                 <Trigger.EnterActions>
89                                     <BeginStoryboard Storyboard="{StaticResource ScrollMouseOverHeight}" />
90                                 </Trigger.EnterActions>
91                             </Trigger>
92                         </ControlTemplate.Triggers>
93                     </ControlTemplate>
94                 </Setter.Value>
95             </Setter>
96         </Trigger>
97     </Style.Triggers>
98 </Style>

 

最后再定义ScrollViewer样式

 1 <Style x:Key="WD.DefaultScrollViewer" TargetType="{x:Type ScrollViewer}">
 2     <Setter Property="SnapsToDevicePixels" Value="True" />
 3     <Setter Property="UseLayoutRounding" Value="True" />
 4     <Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />
 5     <Setter Property="Template">
 6         <Setter.Value>
 7             <ControlTemplate TargetType="{x:Type ScrollViewer}">
 8                 <Grid Background="{TemplateBinding Background}">
 9                     <Grid.ColumnDefinitions>
10                         <ColumnDefinition Width="*" />
11                         <ColumnDefinition Width="Auto" />
12                     </Grid.ColumnDefinitions>
13                     <Grid.RowDefinitions>
14                         <RowDefinition Height="*" />
15                         <RowDefinition Height="Auto" />
16                     </Grid.RowDefinitions>
17                     <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="{TemplateBinding Padding}" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" />
18                     <!--使用自定义样式的ScrollBar(垂直)-->
19                     <ScrollBar x:Name="PART_VerticalScrollBar" Grid.Row="0" Grid.Column="1" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource WD.DefaultScrollBar}"/>
20                     <!--使用自定义样式的ScrollBar(水平)-->
21                     <!--主要是通过设置属性 Orientation="Horizontal"来进行切换-->
22                     <ScrollBar x:Name="PART_HorizontalScrollBar" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource WD.DefaultScrollBar}" />
23                 </Grid>
24             </ControlTemplate>
25         </Setter.Value>
26     </Setter>
27 </Style>

 

运行效果

demo

 

示例代码

https://github.com/zhaotianff/cnblog-demo-code/tree/main/WPFStyleAndTemplateDemo

 

参考资料

https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/controls/how-to-customize-the-thumb-size-on-a-scrollbar

posted @ 2026-09-23 10:00  zhaotianff  阅读(12)  评论(0)    收藏  举报