摘抄自《深入浅出WPF》第九章
WPF命令系统基本要素:
1.命令(Command):WPF命令实际上就是实现ICommand接口的类,用的最多的是RoutedCommand类
2.命令源(Command Source):命令发送者,是实现了ICommandSource接口的类
3.命令目标(Command Target):命令发送目标。命令目标必须是实现了IInputElement接口的类
4.命令关联(Command Binding):负责把一些外围逻辑与命令关联起来。
命令的使用步骤:
1.创建命令类:即获得一个实现ICommand接口的类,如果命令与具体业务逻辑无关则使用WPF类库中的RoutedCommand类即可,否则需要创建RoutedCommand(或ICommand接口)的派生类
2.声明命令实例:创建命令类的实例
3.指定命令源:即指定由谁发送命令。一旦把命令指派给命令源,命令原会受命令的影响,当命令不能被执行的时候作为命令源的控件将处在不可用的状态。
4.指定命令目标:命令目标并不是命令的属性而是命令源的属性,指定命令目标是告诉命令源向哪个组件发送命令,无论这个组件是否拥有焦点它都会收到这个命令。如果没有为命令源指定命令目标,则WPF系统认为当前拥有焦点的对象就是命令目标
5.设置命令关联:WPF命令需要CommandBindng在执行前来帮助判断是不是可以执行,在执行后还要做一些事件。
WPF命令系统基本元素关系图:
实例:定义一个命令,使用Button发送,命令送达TextBox时TextBox会被清空(如果TextBox中没有文字则命令不可被送达,Button按钮不可用)
XAML界面:
<Window x:Class="SimpleCommandTest.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:SimpleCommandTest" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid> <StackPanel x:Name="stackPanel" Margin="5" Background="AliceBlue"> <Button x:Name="button1" Content="Send Command" Margin="5"/> <TextBox x:Name="textBoxA" Margin="5" Height="100" TextWrapping="Wrap"/> </StackPanel> </Grid> </Window>
后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); InitializeCommand(); } //声明并定义命令 private RoutedCommand clearCmd = new RoutedCommand("Clear", typeof(MainWindow)); private void InitializeCommand() { //把命令赋值给命令源 this.button1.Command = this.clearCmd; //指定快捷键 this.clearCmd.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt)); //指定命令目标 this.button1.CommandTarget = this.textBoxA; //创建关联 CommandBinding cb = new CommandBinding(); cb.Command = this.clearCmd; cb.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute); cb.Executed += new ExecutedRoutedEventHandler(cb_Executed); //把命令关联安置在外围控件上 this.stackPanel.CommandBindings.Add(cb); } //当探测命令是否可以执行时,调用 void cb_CanExecute(object sender,CanExecuteRoutedEventArgs e) { if(string.IsNullOrEmpty(this.textBoxA.Text)) { e.CanExecute = false; } else { e.CanExecute = true; } e.Handled = true; } //命令送达目标后,调用 void cb_Executed(object sender,ExecutedRoutedEventArgs e) { this.textBoxA.Clear(); e.Handled = true; } }
WPF命令库:
1.ApplicationCommands
2.ComponentCommands
3.NavigationCommands
4.MediaCommands
5.EditingCommands
带参数的命令:通过参数来决定命令要做什么事。命令源一定是实现了ICommandSource接口的对象,而ICommandSource有一个名为CommandPrameter的属性。
实例:
XAML代码
<Window x:Class="SimpleCommandPrameterTest.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:SimpleCommandPrameterTest" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid Margin="5" Background="AliceBlue"> <Grid.RowDefinitions> <RowDefinition Height="24"/> <RowDefinition Height="4"/> <RowDefinition Height="24"/> <RowDefinition Height="4"> </RowDefinition> <RowDefinition Height="24"/> <RowDefinition Height="4"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock Text="Name:" VerticalAlignment="Center" HorizontalAlignment="Left" /> <TextBox x:Name="nameTextBox" Margin="60,0,0,0" Grid.Row="0"/> <Button Content="New Teacher" Command="New" CommandParameter="Teacher" Grid.Row="2"/> <Button Content="New Student" Command="New" CommandParameter="Student" Grid.Row="4"/> <ListBox x:Name="listBoxNewItems" Grid.Row="6"/> </Grid> <!--为窗口添加CommandBinding--> <Window.CommandBindings> <CommandBinding Command="New" CanExecute="New_CanExecute" Executed="New_Executed"/> </Window.CommandBindings> </Window>
后台代码:
public MainWindow() { InitializeComponent(); } private void New_CanExecute(object sender,CanExecuteRoutedEventArgs e) { if(string.IsNullOrEmpty(this.nameTextBox.Text)) { e.CanExecute = false; } else { e.CanExecute = true; } } private void New_Executed(object sender, ExecutedRoutedEventArgs e) { string name = this.nameTextBox.Text; if(e.Parameter.ToString()=="Teacher") { this.listBoxNewItems.Items.Add(string.Format("New Teacher:{0},学而不厌,诲人不倦。",name)); } if(e.Parameter.ToString()=="Student") { this.listBoxNewItems.Items.Add(string.Format("New Student:{0},好好学习,天天向上。",name)); } this.nameTextBox.Clear(); } }
在XAML中定义了命令不同的参数,参数不同命令执行后结果不同。
自定义命令
首先创建一个WPF工程,在MainWindow.xaml.cs中定义一个接口:
public interface IView { bool IsChanged { get; set; } void SetBinding(); void Refresh(); void Clear(); void Save(); }
每个需要接受命令的组件必须实现这个接口,确保命令可以成功地对它们执行操作
实现ICommand接口:
public class ClearCommand:ICommand { //当命令可执行状态发生改变时,应当被激发 public event EventHandler CanExecuteChanged; //用于判断命令是否可执行(为了方便,暂不实现) public bool CanExecute(object parameter) { throw new NotImplementedException(); } //命令执行,带有与业务相关的Clear逻辑 public void Execute(object parameter) { IView view = parameter as IView; if(view!=null) { view.Clear(); } } }
命令实现了ICommand接口并继承了CanExecuteChanged事件、CanExecute方法和Execute方法。此命令只用到了Execute方法,实现这个方法时,将方法的参数作为命令的目标,如果目标是IView接口派生类,则调用Clear方法
通过实现ICommandSource接口创建自定义命令源:
public class MyCommandSource : UserControl, ICommandSource { //继承自ICommandSource的三个属性 public ICommand Command { get; set; } public object CommandParameter { get; set; } public IInputElement CommandTarget { get; set; } //组件被单击时连带执行命令 protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnPreviewMouseLeftButtonDown(e); //在命令目标上执行命令,或称让命令作用于命令目标 if (this.CommandTarget != null) { this.Command.Execute(this.CommandTarget); } } }
ICommandSource接口包含Command、CommandParameter、CommandTarget三个属性。在此,CommandParameter没有用到,而CommandTarget被当做参数传递给了Command的Execute方法。
创建命令目标:因为ClearCommand命令作用于IView派生类,所以命令目标必须实现IView接口。为工程添加一个用户控件(UserControl)
XAML代码如下:
<UserControl x:Class="UserCommandTest.MiniView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:UserCommandTest" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <StackPanel> <TextBox x:Name="textBox1" Margin="5"/> <TextBox x:Name="textBox2" Margin="5"/> <TextBox x:Name="textBox3" Margin="5"/> <TextBox x:Name="textBox4" Margin="5"/> </StackPanel> </UserControl>
后台代码如下(此类需要继承自IView):
//此类需要继承IView public partial class MiniView : UserControl,IView { public MiniView() { InitializeComponent(); } public bool IsChanged { get; set; } public void SetBinding(){ } public void Refresh() { } public void Save() { } public void Clear() { this.textBox1.Clear(); this.textBox2.Clear(); this.textBox3.Clear(); this.textBox4.Clear(); } }
主窗体的XAML代码:
<Window x:Class="UserCommandTest.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:UserCommandTest" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid> <StackPanel> <local:MyCommandSource x:Name="ctrlClear" Margin="10"> <TextBlock Text="清除" FontSize="16" TextAlignment="Center" Background="LightGreen" Width="80"/> </local:MyCommandSource> <local:MiniView x:Name="miniView"/> </StackPanel> </Grid> </Window>
主窗体后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); ClearCommand clearCmd = new ClearCommand(); this.ctrlClear.Command = clearCmd; this.ctrlClear.CommandTarget = this.miniView; } }
先创建一个ClearCommand命令实例,并赋值给自定义命令源的Command属性,自定义命令源的CommandTarget属性是自定义命令目标MiniView实例。