WPF ScrollViewer 与 ScrollBar 自定义样式

该 XAML 代码是一个 WPF 资源字典(ResourceDictionary),核心作用是自定义 ScrollViewer(滚动视图)和 ScrollBar(滚动条)的外观与交互行为,通过样式覆盖默认控件样式,实现统一、个性化的滚动交互效果,无需在每个使用控件的地方重复定义样式,可直接在项目中引用。

  • 基础外观:

    • 边框透明(BorderThickness=0,取消默认边框)

    • 内部网格布局:包含 “滚动内容区” 和 “垂直 / 水平滚动条”

  • 模板结构(ControlTemplate):

    • 外层边框(Border):承载滚动视图的整体容器

    • 内部网格(Grid):

      1) 滚动的内容(ScrollContentPresenter):显示滚动的内容(绑定 Cursor、Padding 等属性)

      2) 垂直滚动条(PART_VerticalScrollBar):右对齐,绑定 for_scrollbar 样式,可见性由内容高度决定(ComputedVerticalScrollBarVisibility

      3) 水平滚动条(PART_HorizontalScrollBar):底对齐,同上,可见性由内容宽度决定

  • 交互动画(通过 EventTrigger 实现): 是该样式的核心亮点,实现 “滚动时 / 鼠标 hover 时” 的动态透明度变化,提升交互体验:

触发事件 动画效果 细节
ScrollChanged(滚动时) 滚动条透明度 “0.2→0.8→0.2” - 先 1 秒内从 0.2 升到 0.8(突出滚动条)
- 再 1 秒内从 0.8 降回 0.2(滚动停止后恢复低调)
MouseEnter(鼠标移入滚动条) 透明度 0.2→0.8(0.7 秒过渡) 鼠标靠近时,滚动条变清晰,便于操作
MouseLeave(鼠标移出滚动条) 透明度 0.8→0.2(0.7 秒过渡) 鼠标离开后,滚动条恢复半透明,减少视觉干扰

添加资源字典 ScrollViewerStyle.xaml

点击查看代码
<ResourceDictionary xmlns="<http://schemas.microsoft.com/winfx/2006/xaml/presentation>"
                    xmlns:x="<http://schemas.microsoft.com/winfx/2006/xaml>">
    <!-- ScrollViewer 滚动条 -->
    <Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <Setter Property="IsTabStop" Value="false"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Thumb}">
                    <Grid>
                        <!--滚动条颜色-->
                        <Border Background="#646465" CornerRadius="3"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="HorizontalScrollBarPageButton" TargetType="{x:Type RepeatButton}">
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Focusable" Value="false"/>
        <Setter Property="IsTabStop" Value="false"/>
        <Setter Property="Opacity" Value="0.2"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RepeatButton}">
                    <Rectangle Fill="{TemplateBinding Background}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="VerticalScrollBarPageButton" TargetType="{x:Type RepeatButton}">
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Focusable" Value="false"/>
        <Setter Property="IsTabStop" Value="false"/>
        <Setter Property="Opacity" Value="0.2"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RepeatButton}">
                    <Rectangle Fill="{TemplateBinding Background}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!--滚动条上下按钮-->
    <Style x:Key="VerticalScrollBarPageButton2" TargetType="{x:Type RepeatButton}">
        <Setter Property="OverridesDefaultStyle"  Value="true"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Focusable" Value="false"/>
        <Setter Property="IsTabStop" Value="false"/>
        <Setter Property="Opacity"  Value="0"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type RepeatButton}">
                    <Rectangle Fill="#90000000" Width="0" Height="0"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="for_scrollbar" TargetType="{x:Type ScrollBar}">
        <Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
        <Setter Property="Stylus.IsFlicksEnabled" Value="false"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Margin" Value="0,1,1,6"/>
        <Setter Property="Width"  Value="10"/>
        <Setter Property="MinWidth"  Value="5"/>
        <Setter Property="Opacity"   Value="0.2"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ScrollBar}">
                    <Grid x:Name="Bg" SnapsToDevicePixels="true">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="auto"></RowDefinition>
                            <RowDefinition Height="*"></RowDefinition>
                            <RowDefinition Height="auto"></RowDefinition>
                        </Grid.RowDefinitions>
                        <RepeatButton  Grid.Row="0" Style="{StaticResource VerticalScrollBarPageButton2}" Command="{x:Static ScrollBar.PageUpCommand}"/>
                        <Track x:Name="PART_Track"   Grid.Row="1" IsEnabled="{TemplateBinding IsMouseOver}" IsDirectionReversed="true">
                            <Track.DecreaseRepeatButton>
                                <RepeatButton Style="{StaticResource VerticalScrollBarPageButton}" Command="{x:Static ScrollBar.PageUpCommand}"/>
                            </Track.DecreaseRepeatButton>
                            <Track.IncreaseRepeatButton>
                                <RepeatButton Style="{StaticResource VerticalScrollBarPageButton}" Command="{x:Static ScrollBar.PageDownCommand}"/>
                            </Track.IncreaseRepeatButton>
                            <Track.Thumb>
                                <Thumb Style="{StaticResource ScrollBarThumb}"/>
                            </Track.Thumb>
                        </Track>
                        <RepeatButton Grid.Row="2" Style="{StaticResource VerticalScrollBarPageButton2}" Command="{x:Static ScrollBar.PageDownCommand}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="Orientation" Value="Horizontal">
                <Setter Property="Background"  Value="Transparent"/>
                <Setter Property="Margin" Value="1,0,6,1"/>
                <Setter Property="Height"   Value="5"/>
                <Setter Property="MinHeight"  Value="5"/>
                <Setter Property="Width"    Value="Auto"/>
                <Setter Property="Opacity" Value="0.2"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ScrollBar}">
                            <Grid x:Name="Bg" SnapsToDevicePixels="true">
                                <Track x:Name="PART_Track" IsEnabled="{TemplateBinding IsMouseOver}">
                                    <Track.DecreaseRepeatButton>
                                        <RepeatButton Style="{StaticResource HorizontalScrollBarPageButton}"  Command="{x:Static ScrollBar.PageLeftCommand}"/>
                                    </Track.DecreaseRepeatButton>
                                    <Track.IncreaseRepeatButton>
                                        <RepeatButton Style="{StaticResource HorizontalScrollBarPageButton}"  Command="{x:Static ScrollBar.PageRightCommand}"/>
                                    </Track.IncreaseRepeatButton>
                                    <Track.Thumb>
                                        <Thumb Style="{StaticResource ScrollBarThumb}"/>
                                    </Track.Thumb>
                                </Track>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="for_scrollviewer"
           TargetType="{x:Type ScrollViewer}">
        <Setter Property="BorderBrush" Value="LightGray"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ScrollViewer}">
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True">
                        <Grid Background="{TemplateBinding Background}">
                            <ScrollContentPresenter  Cursor="{TemplateBinding Cursor}" Margin="{TemplateBinding Padding}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
                            <ScrollBar x:Name="PART_VerticalScrollBar"
                                       HorizontalAlignment="Right"
                                       Maximum="{TemplateBinding ScrollableHeight}"
                                       Orientation="Vertical"
                                       Style="{StaticResource for_scrollbar}"
                                       ViewportSize="{TemplateBinding ViewportHeight}"
                                       Value="{TemplateBinding VerticalOffset}"
                                       Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
                            <ScrollBar x:Name="PART_HorizontalScrollBar"
                                       Maximum="{TemplateBinding ScrollableWidth}"
                                       Orientation="Horizontal"
                                       Style="{StaticResource for_scrollbar}"
                                       VerticalAlignment="Bottom"
                                       Value="{TemplateBinding HorizontalOffset}"
                                       ViewportSize="{TemplateBinding ViewportWidth}"
                                       Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <EventTrigger RoutedEvent="ScrollChanged" >
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation
                                        Storyboard.TargetName="PART_VerticalScrollBar"
                                        Storyboard.TargetProperty="Opacity"
                                        To="0.8"
                                        Duration="0:0:1"/>
                                    <DoubleAnimation
                                        Storyboard.TargetName="PART_VerticalScrollBar"
                                        Storyboard.TargetProperty="Opacity"
                                        To="0.2"
                                        Duration="0:0:1"
                                        BeginTime="0:0:1"/>
                                    <DoubleAnimation
                                        Storyboard.TargetName="PART_HorizontalScrollBar"
                                        Storyboard.TargetProperty="Opacity"
                                        To="0.8"
                                        Duration="0:0:1"/>
                                    <DoubleAnimation
                                        Storyboard.TargetName="PART_HorizontalScrollBar"
                                        Storyboard.TargetProperty="Opacity"
                                        To="0.2"
                                        Duration="0:0:1"
                                        BeginTime="0:0:1"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="MouseEnter"
                                      SourceName="PART_VerticalScrollBar">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation
                                        Storyboard.TargetName="PART_VerticalScrollBar"
                                        Storyboard.TargetProperty="Opacity"
                                        To="0.8"
                                        Duration="0:0:0.7"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="MouseLeave"
                                      SourceName="PART_VerticalScrollBar">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation
                                        Storyboard.TargetName="PART_VerticalScrollBar"
                                        Storyboard.TargetProperty="Opacity"
                                        To="0.2"
                                        Duration="0:0:0.7"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="MouseEnter"
                                      SourceName="PART_HorizontalScrollBar">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation
                                        Storyboard.TargetName="PART_HorizontalScrollBar"
                                        Storyboard.TargetProperty="Opacity"
                                        To="0.8"
                                        Duration="0:0:0.7"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="MouseLeave"
                                      SourceName="PART_HorizontalScrollBar">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation
                                        Storyboard.TargetName="PART_HorizontalScrollBar"
                                        Storyboard.TargetProperty="Opacity"
                                        To="0.2"
                                        Duration="0:0:0.7"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!--默认ScrollBar-->
    <Style TargetType="ScrollBar" BasedOn="{StaticResource for_scrollbar}"/>

    <!--默认ScrollView-->
    <Style TargetType="ScrollViewer" BasedOn="{StaticResource for_scrollviewer}"/>
</ResourceDictionary>

App.xaml 引入对应资源字典

点击查看代码
 <Application.Resources>
     <ResourceDictionary> 
         <ResourceDictionary.MergedDictionaries>
             <!--  ScrollViewer  -->
             <ResourceDictionary Source="pack://application:,,,/WpfMiniaturesDemo;component/ScrollViewerStyle.xaml" />
         </ResourceDictionary.MergedDictionaries>
     </ResourceDictionary>
 </Application.Resources>
posted @ 2025-09-19 14:28  笺上知微  阅读(80)  评论(0)    收藏  举报