public class ViewModel : ViewModelBase
public ICommand ChangeNameCommand { get; }
接下来,我们将向MainWindow添加一个按钮,并使用Binding将其Command属性设置为视图模型中的命令。
<Button Content="Change Name" Command="{Binding Path=ChangeNameCommand}" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
现在,我们只需要向视图模型中的ChangeNameCommand属性分配一个新的命令对象即可。不幸的是,WPF没有附带适合在视图模型中使用的默认ICommand实现,但是该接口非常简单,可以实现:
public class DelegateCommand : ICommand
private readonly Action<object> _executeAction;
public DelegateCommand(Action<object> executeAction)
_executeAction = executeAction;
public void Execute(object parameter) => _executeAction(parameter);
public bool CanExecute(object parameter) => true;
public event EventHandler CanExecuteChanged;
例如,在这个非常简单的实现中,执行命令时将调用Action委托。现在,我们将忽略接口的CanExecute部分,并始终允许执行命令。
现在,我们可以完成视图模型中的代码。
public class ViewModel : ViewModelBase
private readonly DelegateCommand _changeNameCommand;
public ICommand ChangeNameCommand => _changeNameCommand;
public ViewModel()
_changeNameCommand = new DelegateCommand(OnChangeName);
private void OnChangeName(object commandParameter)
FirstName = "Walter";
运行我们的简单应用程序,我们可以看到单击按钮确实可以更改名称。
接下来,让我们返回并实现ICommand接口的CanExecute部分。
public class DelegateCommand : ICommand
private readonly Action<object> _executeAction;
private readonly Func<object, bool> _canExecuteAction;
public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecuteAction)
_executeAction = executeAction;
_canExecuteAction = canExecuteAction;
public void Execute(object parameter) => _executeAction(parameter);
public bool CanExecute(object parameter) => _canExecuteAction?.Invoke(parameter) ?? true;
public event EventHandler CanExecuteChanged;
public void InvokeCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
与execute方法类似,此命令还将接受CanExecute委托。同样,CanExecuteChanged事件也使用公共方法公开,因此我们可以在CanExecute委托的返回值更改时随时提高它。
回到我们的视图模型中,我们将进行以下补充。
public ViewModel()
_changeNameCommand = new DelegateCommand(OnChangeName, CanChangeName);
private void OnChangeName(object commandParameter)
FirstName = "Walter";
_changeNameCommand.InvokeCanExecuteChanged();
private bool CanChangeName(object commandParameter)
return FirstName != "Walter";
调用CanChangeName以确定命令是否可以执行。在这种情况下,一旦名称更改为“ Walter”,我们将仅阻止命令执行。最后,在OnChangeName方法中更改名称后,该命令通过引发其事件来通知按钮其CanExecute状态已更改。
运行该应用程序,我们可以看到在更改名称后该按钮可以正确禁用。
Getting started with Model-View-ViewModel (MVVM) pattern using Windows Presentation Framework (WPF)
使用AutoUpdater.NET实现自动更新
AutoUpdater.NET是一个.net库,允许winforms和wpf应用实现自动更新,只需要以下几步:
1,定义一个升级文件;
<?xml version="1.0" encoding="UTF-8"?>
<version>2.0.0.0</version>
<url>http://rbsoft.org/downloads/AutoUpdaterTest.zip</url>
<changelog>https://github.com/ravibpatel/AutoUpdater.NET/releases</changelog>
<mandatory>false</mandatory>
</item>
2, 安装 Autoupdater.NET.Official扩展
PM> Install-Package Autoupdater.NET.Official
3,使用只需要在程序加载前加入以下代码即可
protected override void OnStartup(StartupEventArgs e)
AutoUpdater.ParseUpdateInfoEvent += AutoUpdaterOnParseUpdateInfoEvent;
//AutoUpdater.ReportErrors = true;
AutoUpdater.Start("https://api.yunyin.la/file-helper/upgrade");
将xml修改为json
private void AutoUpdaterOnParseUpdateInfoEvent(ParseUpdateInfoEventArgs args)
dynamic json = JsonConvert.DeserializeObject(args.RemoteData);
args.UpdateInfo = new UpdateInfoEventArgs
CurrentVersion = json.version,
DownloadURL = json.url,
Mandatory = new Mandatory
Value = json.mandatory.value,
UpdateMode = json.mandatory.mode,
json样例
"version":"2.0.0.0",
"url":"http://rbsoft.org/downloads/AutoUpdaterTest.zip",
"changelog":"https://github.com/ravibpatel/AutoUpdater.NET/releases",
"mandatory":{
"value":true,
"minVersion": "2.0.0.0",
"mode":1
"checksum":{
"value":"E5F59E50FC91A9E52634FFCB11F32BD37FE0E2F1",
"hashingAlgorithm":"SHA1"
AutoUpdater.NET github
private readonly System.Windows.Forms.NotifyIcon nIcon = new System.Windows.Forms.NotifyIcon();
public MainWindow()
InitializeComponent();
initNotifyIcon();
private void initNotifyIcon()
nIcon.Visible = true;
nIcon.Icon = new Icon("./favicon.ico");
nIcon.Text = "测试程序";
nIcon.MouseClick += new System.Windows.Forms.MouseEventHandler(show_Click);
nIcon.ContextMenu = new System.Windows.Forms.ContextMenu();
System.Windows.Forms.MenuItem show = new System.Windows.Forms.MenuItem("打开");
show.Click += new EventHandler(show_Click);
nIcon.ContextMenu.MenuItems.Add(show);
System.Windows.Forms.MenuItem exit = new System.Windows.Forms.MenuItem("退出");
exit.Click += new EventHandler(exit_Click);
nIcon.ContextMenu.MenuItems.Add(exit);
private void exit_Click(object sender, EventArgs e)
Environment.Exit(0);
private void show_Click(object Sender, EventArgs e)
if (WindowState == WindowState.Minimized)
WindowState = WindowState.Normal;
Show();
Activate();
protected override void OnStateChanged(EventArgs e)
if (WindowState == WindowState.Minimized) Hide();
base.OnStateChanged(e);
protected override void OnClosing(CancelEventArgs e)
e.Cancel = true;
Hide();
base.OnClosing(e);
【C#】窗体最小化到托盘(WinForm和WPF) – CSDN blog
NotifyIcon Class – 微软官方系统托盘类API以及示例
WPF NotifyIcon – 系统托盘高级自定义
<ListView VerticalAlignment="Top" HorizontalAlignment="Stretch">
<ListViewItem>
<TextBlock Text="xxxx1"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx2"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx3"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx4"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx5"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx6"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx7"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx8"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx9"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx10"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx11"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx12"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx13"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx14"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx15"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx16"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx17"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx18"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx19"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx20"/>
</ListViewItem>
</ListView>
</Grid>
Windows 风格
<Style x:Key="ScrollBarLineButton"
TargetType="{x:Type RepeatButton}">
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="OverridesDefaultStyle"
Value="true" />
<Setter Property="Focusable"
Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border x:Name="Border"
Margin="1"
CornerRadius="2"
BorderThickness="1">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource BorderMediumColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource BorderDarkColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource ControlLightColor}"/>
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops)[1].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="{StaticResource ControlPressedColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Shape.Fill).
(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="{StaticResource DisabledForegroundColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="Arrow"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{Binding Content,
RelativeSource={RelativeSource TemplatedParent}}" >
<Path.Fill>
<SolidColorBrush Color="{DynamicResource GlyphColor}"/>
</Path.Fill>
</Path>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ScrollBarPageButton"
TargetType="{x:Type RepeatButton}">
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="OverridesDefaultStyle"
Value="true" />
<Setter Property="IsTabStop"
Value="false" />
<Setter Property="Focusable"
Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Background="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ScrollBarThumb"
TargetType="{x:Type Thumb}">
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="OverridesDefaultStyle"
Value="true" />
<Setter Property="IsTabStop"
Value="false" />
<Setter Property="Focusable"
Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border CornerRadius="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="VerticalScrollBar"
TargetType="{x:Type ScrollBar}">
<Grid.RowDefinitions>
<RowDefinition MaxHeight="18" />
<RowDefinition Height="0.00001*" />
<RowDefinition MaxHeight="18" />
</Grid.RowDefinitions>
<Border Grid.RowSpan="3"
CornerRadius="2"
Background="#F0F0F0" />
<RepeatButton Grid.Row="0"
Style="{StaticResource ScrollBarLineButton}"
Height="18"
Command="ScrollBar.LineUpCommand"
Content="M 0 4 L 8 4 L 4 0 Z" />
<Track x:Name="PART_Track"
Grid.Row="1"
IsDirectionReversed="true">
<Track.DecreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageUpCommand" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb}"
Margin="1,0,1,0">
<Thumb.BorderBrush>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource BorderLightColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource BorderDarkColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Thumb.BorderBrush>
<Thumb.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Thumb.Background>
</Thumb>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageDownCommand" />
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton Grid.Row="2"
Style="{StaticResource ScrollBarLineButton}"
Height="18"
Command="ScrollBar.LineDownCommand"
Content="M 0 0 L 4 4 L 8 0 Z" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="HorizontalScrollBar"
TargetType="{x:Type ScrollBar}">
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="18" />
<ColumnDefinition Width="0.00001*" />
<ColumnDefinition MaxWidth="18" />
</Grid.ColumnDefinitions>
<Border Grid.ColumnSpan="3"
CornerRadius="2"
Background="#F0F0F0" />
<RepeatButton Grid.Column="0"
Style="{StaticResource ScrollBarLineButton}"
Width="18"
Command="ScrollBar.LineLeftCommand"
Content="M 4 0 L 4 8 L 0 4 Z" />
<Track x:Name="PART_Track"
Grid.Column="1"
IsDirectionReversed="False">
<Track.DecreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageLeftCommand" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb}"
Margin="0,1,0,1">
<Thumb.BorderBrush>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource BorderLightColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource BorderDarkColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Thumb.BorderBrush>
<Thumb.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Thumb.Background>
</Thumb>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageRightCommand" />
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton Grid.Column="2"
Style="{StaticResource ScrollBarLineButton}"
Width="18"
Command="ScrollBar.LineRightCommand"
Content="M 0 0 L 4 4 L 0 8 Z" />
</Grid>
</ControlTemplate>
<Style x:Key="{x:Type ScrollBar}"
TargetType="{x:Type ScrollBar}">
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="OverridesDefaultStyle"
Value="true" />
<Style.Triggers>
<Trigger Property="Orientation"
Value="Horizontal">
<Setter Property="Width"
Value="Auto" />
<Setter Property="Height"
Value="18" />
<Setter Property="Template"
Value="{StaticResource HorizontalScrollBar}" />
</Trigger>
<Trigger Property="Orientation"
Value="Vertical">
<Setter Property="Width"
Value="18" />
<Setter Property="Height"
Value="Auto" />
<Setter Property="Template"
Value="{StaticResource VerticalScrollBar}" />
</Trigger>
</Style.Triggers>
</Style>
需要的资源
<!--Control colors.-->
<Color x:Key="WindowColor">#FFE8EDF9</Color>
<Color x:Key="ContentAreaColorLight">#FFC5CBF9</Color>
<Color x:Key="ContentAreaColorDark">#FF7381F9</Color>
<Color x:Key="DisabledControlLightColor">#FFE8EDF9</Color>
<Color x:Key="DisabledControlDarkColor">#FFC5CBF9</Color>
<Color x:Key="DisabledForegroundColor">#FF888888</Color>
<Color x:Key="SelectedBackgroundColor">#FFC5CBF9</Color>
<Color x:Key="SelectedUnfocusedColor">#FFDDDDDD</Color>
<Color x:Key="ControlLightColor">White</Color>
<Color x:Key="ControlMediumColor">#FF7381F9</Color>
<Color x:Key="ControlDarkColor">#FF211AA9</Color>
<Color x:Key="ControlMouseOverColor">#FF3843C4</Color>
<Color x:Key="ControlPressedColor">#FF211AA9</Color>
<Color x:Key="GlyphColor">#FF444444</Color>
<Color x:Key="GlyphMouseOver">sc#1, 0.004391443, 0.002428215, 0.242281124</Color>
<!--Border colors-->
<Color x:Key="BorderLightColor">#FFCCCCCC</Color>
<Color x:Key="BorderMediumColor">#FF888888</Color>
<Color x:Key="BorderDarkColor">#FF444444</Color>
<Color x:Key="PressedBorderLightColor">#FF888888</Color>
<Color x:Key="PressedBorderDarkColor">#FF444444</Color>
<Color x:Key="DisabledBorderLightColor">#FFAAAAAA</Color>
<Color x:Key="DisabledBorderDarkColor">#FF888888</Color>
<Color x:Key="DefaultBorderBrushDarkColor">Black</Color>
<!--Control-specific resources.-->
<Color x:Key="HeaderTopColor">#FFC5CBF9</Color>
<Color x:Key="DatagridCurrentCellBorderColor">Black</Color>
<Color x:Key="SliderTrackDarkColor">#FFC5CBF9</Color>
<Color x:Key="NavButtonFrameColor">#FF3843C4</Color>
<LinearGradientBrush x:Key="MenuPopupBrush"
EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="0.5" />
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill"
StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#000000FF"
Offset="0" />
<GradientStop Color="#600000FF"
Offset="0.4" />
<GradientStop Color="#600000FF"
Offset="0.6" />
<GradientStop Color="#000000FF"
Offset="1" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
Apple风格
<!--Scrollbar Thumbs-->
<Style x:Key="ScrollThumbs" TargetType="{x:Type Thumb}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Grid x:Name="Grid">
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" Fill="Transparent" />
<Border x:Name="Rectangle1" CornerRadius="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" Background="{TemplateBinding Background}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Tag" Value="Horizontal">
<Setter TargetName="Rectangle1" Property="Width" Value="Auto" />
<Setter TargetName="Rectangle1" Property="Height" Value="7" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--ScrollBars-->
<Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
<Setter Property="Stylus.IsFlicksEnabled" Value="false" />
<Setter Property="Foreground" Value="#8C8C8C" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Width" Value="8" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="GridRoot" Width="8" Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="0.00001*" />
</Grid.RowDefinitions>
<Track x:Name="PART_Track" Grid.Row="0" IsDirectionReversed="true" Focusable="false">
<Track.Thumb>
<Thumb x:Name="Thumb" Background="{TemplateBinding Foreground}" Style="{DynamicResource ScrollThumbs}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton x:Name="PageUp" Command="ScrollBar.PageDownCommand" Opacity="0" Focusable="false" />
</Track.IncreaseRepeatButton>
<Track.DecreaseRepeatButton>
<RepeatButton x:Name="PageDown" Command="ScrollBar.PageUpCommand" Opacity="0" Focusable="false" />
</Track.DecreaseRepeatButton>
</Track>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="Thumb" Property="IsMouseOver" Value="true">
<Setter Value="{DynamicResource ButtonSelectBrush}" TargetName="Thumb" Property="Background" />
</Trigger>
<Trigger SourceName="Thumb" Property="IsDragging" Value="true">
<Setter Value="{DynamicResource DarkBrush}" TargetName="Thumb" Property="Background" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Thumb" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="Orientation" Value="Horizontal">
<Setter TargetName="GridRoot" Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90" />
</Setter.Value>
</Setter>
<Setter TargetName="PART_Track" Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90" />
</Setter.Value>
</Setter>
<Setter Property="Width" Value="Auto" />
<Setter Property="Height" Value="8" />
<Setter TargetName="Thumb" Property="Tag" Value="Horizontal" />
<Setter TargetName="PageDown" Property="Command" Value="ScrollBar.PageLeftCommand" />
<Setter TargetName="PageUp" Property="Command" Value="ScrollBar.PageRightCommand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
ScrollBar 样式和模板
APPLE STYLE SCROLLBAR IN WPF
ScrollBar 类
Track 类