创建一个类似 Blend 的滚动条






4.88/5 (19投票s)
本文展示了如何将滚动条样式化,
引言
我正在构建一个消息应用程序。该应用程序是一个用 C# 编写的 SOA 桌面应用程序,使用了 WPF。在某个时候,我想如果我的应用程序有一个像 Expression Blend 那样基于黑色的主题就好了。我从滚动条开始,这也是我将在本文中讨论的内容:如何样式化滚动条,使其看起来像 Expression Blend 中的滚动条。我样式化了水平和垂直滚动条。可下载的代码还包括一个使用这两个滚动条的列表框的样式(以便滚动条看起来更好 :))。
背景
我猜读者应该对样式和模板有基本的了解。如果读者还要阅读列表框的样式,他们还应该了解一些关于动画的基本知识。
笔刷
示例使用了以下笔刷:
<SolidColorBrush x:Key="StandardBorderBrush" Color="DarkGray"></SolidColorBrush>
<SolidColorBrush x:Key="StandardBrush" Color="LightGray"></SolidColorBrush>
<SolidColorBrush x:Key="PressedBrush" Color="Gray"></SolidColorBrush>
<SolidColorBrush x:Key="HoverBrush" Color="#fefefe"></SolidColorBrush>
<SolidColorBrush x:Key="GlyphBrush" Color="333333"></SolidColorBrush>
滚动条结构
正如我们大多数人所知,滚动条不像按钮那样容易样式化。这是因为滚动条大约有 6 个需要处理的组件。您可以在下面的图片中看到它们

为了正确地样式化滚动条,我使用了以下样式:用于一次移动数据一个单位的重复按钮(每个重复按钮一个样式)、用于滑块的样式以及用于透明重复按钮的样式(两者共用一个)。
最后,滚动条看起来会像下面的图片。左边可以看到整个窗口,右边可以看到滚动条的 3 种状态:正常、悬停和按下。
![]() |
![]() |
垂直滚动条
此滚动条使用 3 行网格定义。向上按钮位于第一行,轨道位于第二行,向下按钮位于第三行。向上按钮的样式可以在下面的标记中看到:
<Style x:Key="LineButtonUpStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Grid Margin="1" Height="18" >
<Path Stretch="None"
HorizontalAlignment="Center"
VerticalAlignment="Center" Name="Path"
Fill="{StaticResource
StandardBrush}"
Data="M0 0L 8 8 L 4 0 Z"></Path>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Path" Property="Fill"
Value="{StaticResource HoverBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Path" Property="Fill"
Value="{StaticResource PressedBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
如您所见,此按钮显示一个代表三角形的路径元素。使用对齐属性将此三角形放置在按钮的中心。此按钮还有 2 个触发器。当鼠标悬停在按钮上时,三角形会获得一个较亮的笔刷 (HoverBrush
),当按钮被按下时,三角形会被赋予一个较暗的笔刷 (PressedBrush
)。
向下按钮的样式非常相似。唯一的区别是三角形的方向。这个指向下方。样式可以在下面看到。差异部分为粗体。
<Style x:Key="LineButtonDownStyle"
TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable"
Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type
RepeatButton}">
<Grid Margin="1"
Height="18" >
<Path Stretch="None" HorizontalAlignment="Center"
VerticalAlignment="Center" Name="Path" Fill="{StaticResource
StandardBrush}"
Data="M 0 0 L 4 8 L 8 0 Z"></Path></Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Path" Property="Fill"
Value="{StaticResource HoverBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Path" Property="Fill"
Value="{StaticResource PressedBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
另外两个重复按钮(透明按钮)的样式非常简单。它将按钮的模板设置为一个透明边框。您可以在下面看到此样式
<Style x:Key="ScrollBarPageButtonStyle"
TargetType="{x:Type RepeatButton}">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Background="Transparent"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
滑块的样式可以在下面看到
<Style x:Key="VerticalScrollBarThumbStyle"
TargetType="{x:Type Thumb}">
<Setter Property="IsTabStop"
Value="False"/>
<Setter Property="Focusable"
Value="False"/>
<Setter Property="Margin"
Value="1,0,1,0" />
<Setter Property="BorderBrush" Value="{StaticResource StandardBorderBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Rectangle Width="8" Name="ellipse" Stroke="{StaticResource
StandardBorderBrush}"
Fill="{StaticResource StandardBrush}"
RadiusX="5" RadiusY="5"></Rectangle>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="ellipse"
Property="Fill" Value="{StaticResource HoverBrush}"></Setter>
</Trigger>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="ellipse"
Property="Fill" Value="{StaticResource PressedBrush}"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
从上面的样式可以看出,滑块是一个 8 像素宽的矩形,命名为 ellipse(因为我最初想使用一个椭圆但没有更改它)。滑块也有 2 个触发器。当鼠标悬停在滑块上时,滑块会获得一个较亮的笔刷 (HoverBrush
)。当用户拖动滑块时,它会获得一个较暗的笔刷 (PressedBrush
)。
为了构建滚动条,需要使用以上所有样式。正如我之前所说,滚动条是一个具有非常微妙的线性渐变背景的三行网格。
<ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="18"/>
<RowDefinition Height="*"/>
<RowDefinition MaxHeight="18"/>
</Grid.RowDefinitions>
<Grid.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#4c4c4c"></GradientStop>
<GradientStop Offset="1" Color="#434343"></GradientStop>
</LinearGradientBrush>
</Grid.Background>
在第一行,我们有向上按钮。
<RepeatButton Grid.Row="0" Height="18"
Style="{StaticResource LineButtonUpStyle}"
Command="ScrollBar.LineUpCommand">
</RepeatButton>
如您所见,这是一个重复按钮,它使用了之前讨论的 LineButtonUpStyle
(带向上指向三角形的样式)。
在第二行,我们有滚动条的轨道。此轨道必须有一个特殊的名称 (PART_Track
)。
<Track Name="PART_Track" Grid.Row="1" IsDirectionReversed="True">
<Track.DecreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageUpCommand"
Style="{StaticResource ScrollBarPageButtonStyle}">
</RepeatButton>
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource VerticalScrollBarThumbStyle}">
</Thumb>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageDownCommand"
Style="{StaticResource ScrollBarPageButtonStyle}">
</RepeatButton>
</Track.IncreaseRepeatButton>
</Track>
从上面的 XAML 可以看出,轨道有 3 个部分。顶部的值减小部分,中间的滑块,底部的值增加部分。增加和减小部分使用了一个使用透明样式的重复按钮。滑块使用其先前描述的自己的样式。
在这里我需要提到另一件事:IsDirectionReversed
属性。看起来如果我没有将其设置为 true
,滑块会从滚动条的底部开始。
最后,在最后一行,我们有另一个重复按钮(指向下方的三角形按钮)。
<RepeatButton Grid.Row="2" Height="18" Style="{StaticResource LineButtonDownStyle}"
Command="ScrollBar.LineDownCommand">
</RepeatButton>
水平滚动条
水平滚动条的结构类似。下面的 XAML 向您展示了模板的标记。
<ControlTemplate x:Key="HorizontalScrollBar"
TargetType="{x:Type ScrollBar}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="18"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition MaxWidth="18"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Offset="0" Color="#4c4c4c"></GradientStop>
<GradientStop Offset="1" Color="#434343"></GradientStop>
</LinearGradientBrush>
</Grid.Background>
<RepeatButton Grid.Column="0" Width="18"
Style="{StaticResource LineButtonLeftStyle}"
Command="ScrollBar.LineLeftCommand">
</RepeatButton>
<Track Name="PART_Track"
Grid.Column="1" IsDirectionReversed="False" >
<Track.DecreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageLeftCommand"
Style="{StaticResource
ScrollBarPageButtonStyle}">
</RepeatButton>
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource HorizontalScrollBarThumbStyle}">
</Thumb>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageRightCommand"
Style="{StaticResource ScrollBarPageButtonStyle}">
</RepeatButton>
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton Grid.Column="2" Width="18"
Style="{StaticResource
LineButtonRightStyle}"
Command="ScrollBar.LineRightCommand">
</RepeatButton>
</Grid>
</ControlTemplate>
如您所见,这次我们有一个 3 列网格。第一列包含向左滚动的重复按钮,第二列包含轨道,最后一列包含向右滚动的重复按钮。这里使用的样式与垂直滚动条使用的样式几乎相同。触发器完全相同。唯一的区别是三角形的方向(它们分别指向左和右),以及滑块矩形的尺寸。这里最后要提到的一点是,IsDirectionReversed
属性设置为 false
。
滚动条样式
这两个模板将用于设置滚动条的模板。这是通过样式应用的。您可以在下面看到
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Style.Triggers>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Width" Value="18"/>
<Setter Property="Height" Value="Auto" />
<Setter Property="Template" Value="{StaticResource VerticalScrollBar}" />
</Trigger>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Width" Value="Auto"/>
<Setter Property="Height" Value="18" />
<Setter Property="Template" Value="{StaticResource HorizontalScrollBar}" />
</Trigger>
</Style.Triggers></Style>
从上面的 XAML 可以看出,如果方向是垂直的,那么垂直滚动条的样式将使用 VerticalScrollBar
模板,如果方向是水平的,则使用 HorizontalScrollBar
模板。最后要提到的一点是,我选择为 4 个滚动按钮(左、右、上、下)使用单独的模板,因为三角形的笔刷需要改变。如果相反,按钮的背景需要改变,这可以通过单个模板(如透明按钮)来完成。
关于列表框样式的一些说明
另外,请查看列表框的样式,因为它们包含一些非常酷的东西。一种样式化列表框右下角矩形(位于两个滚动条交叉处)的方法。
如果您查看滚动查看器的默认模板,您会注意到这个矩形被命名为“corner”,并使用具有以下键的笔刷
x:Key="{x:Static SystemColors.ControlBrushKey}"
模板添加了一个同名资源来覆盖默认颜色。这利用了 WPF 从元素向外搜索资源的方式。就是这样。希望您喜欢这篇文章。
历史
- 添加于 2009 年 8 月 26 日星期三