WPF 修改控件默认的ScrollView样式
WPF中控件自带的ScrollView样式不太好看,可以用下面方式进行修改,本例以ListBox和TreeView控件为例,因为新的ScrollView需要鼠标停留在上面才会显示,可以看下图右边的TreeView中的ScrollView样式:
首先看一下程序结构:
新建一个ScrollView文件夹,在里面添加一个自定义控件ZScrollView,
ZScrollView代码如下:
namespace WpfApp11.ScrollView
{
public class ZScrollViewer : ScrollViewer
{
#region Private属性
#endregion
#region 依赖属性定义
#endregion
#region 依赖属性set get
public double VerticalOffsetEx
{
get { return (double)GetValue(VerticalOffsetExProperty); }
set { SetValue(VerticalOffsetExProperty, value); }
}
public static readonly DependencyProperty VerticalOffsetExProperty =
DependencyProperty.Register("VerticalOffsetEx", typeof(double), typeof(ZScrollViewer), new PropertyMetadata(0d, VerticalOffsetExChangedCallback));
private static void VerticalOffsetExChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ZScrollViewer scrollViewer = d as ZScrollViewer;
scrollViewer.ScrollToVerticalOffset((double)e.NewValue);
}
#endregion
#region Constructors
static ZScrollViewer()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ZScrollViewer), new FrameworkPropertyMetadata(typeof(ZScrollViewer)));
}
#endregion
#region Override方法
#endregion
#region Private方法
public void aa()
{
}
#endregion
}
}
其样式文件Generic.xaml中代码:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp11"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:localSV="clr-namespace:WpfApp11.ScrollView">
<sys:Double x:Key="ScrollBar.Thumb.Width">6</sys:Double>
<SolidColorBrush x:Key="ScrollThumb_Background">#90686868</SolidColorBrush>
<SolidColorBrush x:Key="ScrollThumb_MouseOver_Background">#686868</SolidColorBrush>
<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>
<Rectangle x:Name="thumbRect"
Fill="{StaticResource ScrollThumb_Background}"
RadiusX="3" RadiusY="3" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="thumbRect" Property="Fill" Value="{StaticResource ScrollThumb_MouseOver_Background}" />
</Trigger>
</ControlTemplate.Triggers>
</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" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Rectangle Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}" />
</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" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Rectangle Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}" />
</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,1" />
<Setter Property="Width" Value="{StaticResource ScrollBar.Thumb.Width}" />
<Setter Property="MinWidth" Value="5" />
<Setter Property="Opacity" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="Bg" SnapsToDevicePixels="true">
<Track x:Name="PART_Track" IsDirectionReversed="true"
IsEnabled="{TemplateBinding IsMouseOver}">
<Track.DecreaseRepeatButton>
<RepeatButton Command="{x:Static ScrollBar.PageUpCommand}" Style="{StaticResource VerticalScrollBarPageButton}" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Command="{x:Static ScrollBar.PageDownCommand}" Style="{StaticResource VerticalScrollBarPageButton}" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb}" />
</Track.Thumb>
</Track>
</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" />
<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 Command="{x:Static ScrollBar.PageLeftCommand}" Style="{StaticResource HorizontalScrollBarPageButton}" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Command="{x:Static ScrollBar.PageRightCommand}" Style="{StaticResource HorizontalScrollBarPageButton}" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb}" />
</Track.Thumb>
</Track>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<!-- ScrollViewer -->
<Style TargetType="{x:Type localSV:ZScrollViewer}">
<Setter Property="BorderBrush" Value="LightGray" />
<Setter Property="BorderThickness" Value="0" />
<!--<Setter Property="HorizontalContentAlignment" Value="Left" />-->
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
<!--<Setter Property="VerticalContentAlignment" Value="Top" />-->
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type localSV:ZScrollViewer}">
<Border x:Name="Bd" Background="Transparent"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<Grid Background="{TemplateBinding Background}">
<ScrollContentPresenter Margin="{TemplateBinding Padding}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Cursor="{TemplateBinding Cursor}" />
<ScrollBar x:Name="PART_VerticalScrollBar" HorizontalAlignment="Right"
Maximum="{TemplateBinding ScrollableHeight}"
Orientation="Vertical"
Style="{StaticResource for_scrollbar}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Value="{TemplateBinding VerticalOffset}" />
<ScrollBar x:Name="PART_HorizontalScrollBar" VerticalAlignment="Bottom"
Maximum="{TemplateBinding ScrollableWidth}"
Orientation="Horizontal"
Style="{StaticResource for_scrollbar}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
Value="{TemplateBinding HorizontalOffset}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<!--<EventTrigger RoutedEvent="ScrollChanged">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" Storyboard.TargetName="PART_VerticalScrollBar" Storyboard.TargetProperty="Opacity" To="1" />
<DoubleAnimation BeginTime="0:0:1" Duration="0:0:1" Storyboard.TargetName="PART_VerticalScrollBar" Storyboard.TargetProperty="Opacity" To="0" />
<DoubleAnimation Duration="0:0:1" Storyboard.TargetName="PART_HorizontalScrollBar" Storyboard.TargetProperty="Opacity" To="1" />
<DoubleAnimation BeginTime="0:0:1" Duration="0:0:1" Storyboard.TargetName="PART_HorizontalScrollBar" Storyboard.TargetProperty="Opacity" To="0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>-->
<EventTrigger RoutedEvent="MouseEnter" SourceName="Bd">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="PART_VerticalScrollBar" Storyboard.TargetProperty="Opacity" To="1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave" SourceName="Bd">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:0" Duration="0:0:0.3" Storyboard.TargetName="PART_VerticalScrollBar" Storyboard.TargetProperty="Opacity"
To="0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseEnter" SourceName="Bd">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="PART_HorizontalScrollBar" Storyboard.TargetProperty="Opacity" To="1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave" SourceName="Bd">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="0:0:1" Duration="0:0:0.5" Storyboard.TargetName="PART_HorizontalScrollBar" Storyboard.TargetProperty="Opacity"
To="0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
在App.xaml中添加这个样式的引用:
<Application x:Class="WpfApp11.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp11"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ScrollView/Themes/Generic.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
然后添加一个Dept类,这个类只是为TreeView提供数据源的类:
public class Dept
{
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
private string _ID;
public string ID
{
get { return _ID; }
set { _ID = value; }
}
private ObservableCollection<Dept> _Children;
public ObservableCollection<Dept> Children
{
get { return _Children; }
set { _Children = value; }
}
}
主要关键步骤:
我们在主窗体的Xaml页面中对ListBox控件和TreeView控件添加模板(文档大纲->编辑模板->编辑副本->确定),然后修改控件模板中的ScrollView为ZScrollView;
<Window x:Class="WpfApp11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp11"
xmlns:localSV="clr-namespace:WpfApp11.ScrollView"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<SolidColorBrush x:Key="ListBox.Static.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ListBox.Static.Border" Color="#FFABADB3"/>
<SolidColorBrush x:Key="ListBox.Disabled.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ListBox.Disabled.Border" Color="#FFD9D9D9"/>
<Style x:Key="ListBoxStyle1" TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="{StaticResource ListBox.Static.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource ListBox.Static.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
<!--<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>-->
<localSV:ZScrollViewer Focusable="False" Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</localSV:ZScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{StaticResource ListBox.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource ListBox.Disabled.Border}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="ListBorder" Color="#828790"/>
<Style x:Key="TreeViewStyle1" TargetType="{x:Type TreeView}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource ListBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeView}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
<!--<ScrollViewer x:Name="_tv_scrollviewer_" Background="{TemplateBinding Background}" CanContentScroll="false" Focusable="false" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
<ItemsPresenter/>
</ScrollViewer>-->
<localSV:ZScrollViewer x:Name="_tv_scrollviewer_" Background="{TemplateBinding Background}" CanContentScroll="false" Focusable="false" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
<ItemsPresenter/>
</localSV:ZScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
<Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
<Setter Property="CanContentScroll" TargetName="_tv_scrollviewer_" Value="true"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" Name="listBox" Width="300" Background="AliceBlue" HorizontalAlignment="Left" Style="{DynamicResource ListBoxStyle1}"></ListBox>
<TreeView Grid.Column="1" Name="treeView" Width="300" Background="AliceBlue" HorizontalAlignment="Left" Style="{DynamicResource TreeViewStyle1}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{Binding Name}" Margin="5,0,0,0" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
最后主窗体的后台代码如下:
namespace WpfApp11
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//ListBox
this.listBox.ItemsSource = GetNumber();
ObservableCollection<Dept> datas = new ObservableCollection<Dept>();
for (int i = 0; i < 10; i++)
{
Dept dept = new Dept();
dept.ID = i.ToString();
dept.Name = "第一级" + i;
if (i % 2 == 0)
{
dept.Children = new ObservableCollection<Dept>();
for (int j = 0; j < 5; j++)
{
Dept child = new Dept();
child.ID = i.ToString() + j.ToString();
child.Name = "第二级" + i.ToString() + j.ToString();
if (j % 2 == 0)
{
child.Children = new ObservableCollection<Dept>();
for (int k = 0; k < 2; k++)
{
Dept three = new Dept();
three.ID = i.ToString() + j.ToString() + k.ToString();
three.Name = "第二级" + i.ToString() + j.ToString() + k.ToString();
child.Children.Add(three);
}
}
dept.Children.Add(child);
}
}
datas.Add(dept);
}
//var items = Enumerable.Range(1, 50).GroupBy(i => i.ToString()[0]);
this.treeView.ItemsSource = datas;
}
private List<int> GetNumber()
{
List<int> listNumber = new List<int>();
for (int i = 0; i < 99; i++)
{
listNumber.Add(i);
}
return listNumber;
}
}
}