第九章 深入浅出话命令

tech2025-05-05  3

第九章 深入浅出话命令

事件的作用是发布、传播一些消息,消息送达接收者,事件的使命也就完成了,每个接收者使用自己的行为来相应事件。事件不具有约束力,命令具有约束力,不仅可以约束代码,还可以约束步骤逻辑。

命令:ICommand接口的类,常用的是RoutedCommand类,也可以自定义类。命令源:命令的发送者,ICommandSource接口的类。命令目标:命令发给谁,或者命令作用在谁身上。IInputElement接口的类。命令关联:把一些外围逻辑与命令关联起来,如进行逻辑判断、后续处理等。

示例:用Button发送一个命令,当命令到达TextBox时TextBox会被清空(如果TextBox中没有文字则命令不可被发送)。

<Window x:Class="WpfApplication1_2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="LightBlue" Title="MainWindow" Height="175" Width="260"> <StackPanel x:Name="stackPanel"> <Button x:Name="button1" Content="Send Command" Margin="5"/> <TextBox x:Name="textBoxA" Margin="5" Height="100"/> </StackPanel> </Window> namespace WpfApplication1_2 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); InitializeCommand();// } //声明并定义命令,一处声明、处处使用 private RoutedCommand clear_Cmd = new RoutedCommand("Clear", typeof(MainWindow)); private void InitializeCommand() { //把命令赋值给发送者(命令源)并指定快捷键 this.button1.Command = this.clear_Cmd; this.clear_Cmd.InputGestures.Add(new KeyGesture(Key.C,ModifierKeys.Alt)); //指定命令目标 this.button1.CommandTarget = this.textBoxA; //创建命令关联 CommandBinding cb = new CommandBinding(); cb.Command = this.clear_Cmd;//只关注与clear_Cmd相关的事件 cb.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute); cb.Executed += new ExecutedRoutedEventHandler(cv_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 cv_Executed(object sender, ExecutedRoutedEventArgs e) { this.textBoxA.Clear(); //避免继续向上传而降低程序性能 e.Handled = true; } } }

RoutedCommand只负责在程序中“跑腿”而并不对命令目标做任何操作,由CommandBinding完成操作,CommandBinding一定要设置在命令目标的外围控件上,不然无法捕捉cb_CanExecute、cv_Executed。

WPF 的命令库

WPF类库里的便捷命令库(都是静态类):

ApplicationCommandsComponentCommandsNavigationCommandsMediaCommandsEditingCommands

示例:界面上有两个按钮,一个新建Teacher档案、一个新建Student档案。都使用New命令,但通过CommandParameter来区别新建类别的不同。(这里使用XAML为窗体添加CommandBinding、CanExecute、Executed)

<Window x:Class="WpfApplication1_2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="240" Width="360" Background="LightBlue" WindowStyle="ToolWindow"> <Grid Margin="6"> <Grid.RowDefinitions> <RowDefinition Height="24"/> <RowDefinition Height="4"/> <RowDefinition Height="24"/> <RowDefinition Height="4"/> <RowDefinition Height="24"/> <RowDefinition Height="4"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--下面代码是命令和命令参数--> <TextBlock Text="Name:" VerticalAlignment="Center" HorizontalAlignment="Left" Grid.Row="0"/> <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> <!--下列代码为窗体添加CommandBindings--> <Window.CommandBindings> <CommandBinding Command="New" CanExecute="New_CanExecute" Executed="New_Executed"/> </Window.CommandBindings> </Window> namespace WpfApplication1_2 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : 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; //如果是Teacher if(e.Parameter.ToString()=="Teacher") { this.listBoxNewItems.Items.Add(string.Format("New Teacher:{0},以其昭昭使人昭昭。", name)); } //如果是Student if(e.Parameter.ToString()=="Student") { this.listBoxNewItems.Items.Add(string.Format("New Student:{0},童蒙!",name)); } } } }
近观命令

ICommand接口与RoutedCommand,这一章节没有仔细看。

自定义Command

WPF命令库中没有自己需要的命令,需要声明定义自己的RoutedCommand实例,这是浅层次的理解,本质还是RoutedCommand的使用(WPF自带的命令源和CommandBinding就是专门为RoutedCommand编写)。

从实现ICommand接口开始,定义自己的命令,并把某些业务逻辑也包含在命令中(想使用自己的ICommand派生类就必须连命令源一起实现)。

为了简化CommandBinding的程序结构,将业务逻辑移入命令的Execute方法内。

//自定义接口 public interface IView //需要将其放在MainWindow的外面,这样其他的窗体才可以引用 { //属性 bool IsChanged { get; set; } //方法 void SetBinding(); void Refresh(); void Clear(); void Save(); //.... } //自定义命令源:WPF命令源是为RoutedCommand准备的,且不可重写。只能通过ICommandSource接口创建自己的命令源 namespace WpfApplication1_2 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public class MyCommandSource : UserControl, ICommandSource { //继承ICommandSource的三个属性: public ICommand Command { get; set; } public object CommandParameter { get; set; }//本例中没有用到 public IInputElement CommandTarget { get; set; }//被当作参数传递给了Command的Execute方法 //在组件被单击时连带执行命令 //为命令选择合适时机,此处为控件被左单击时执行 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); //在命令目标上执行命令,或者让命令作用于命令目标 if (this.CommandTarget != null) { this.Command.Execute(this.CommandTarget); } } } public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); //声明命令并使用命令源和目标与之关联 ClearCommand clearCmd = new ClearCommand(); this.ctrlClear.Command = clearCmd; this.ctrlClear.CommandTarget = this.miniView; } //自定义命令:实现ICommand接口,创建一个专门作用于IView派生类的命令 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方法 } //至此,命令和命令源都有了。差一个可用的命令目标。因为此处ClearCommand作用于IView派生类,所以 //ClearCommand需要实现IView接口:XAML实现UI部分,C#实现接口部分。 } } <Window x:Class="WpfApplication1_2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1_2" Title="MainWindow" Height="205" Width="250" Background="LightBlue" WindowStyle="ToolWindow"> <StackPanel> <local:MyCommandSource x:Name="ctrlClear" Margin="10"> <TextBlock Text="清除" FontSize="16" TextAlignment="Center" Background="LawnGreen" Width="80"/> </local:MyCommandSource> <local:MiniView x:Name="miniView"/> </StackPanel> </Window> <UserControl x:Class="WpfApplication1_2.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" mc:Ignorable="d" d:DesignHeight="114" d:DesignWidth="200"> <Border CornerRadius="5" BorderBrush="LawnGreen" BorderThickness="2"> <StackPanel> <TextBox x:Name="textBox1" Margin="5"/> <TextBox x:Name="textBox2" Margin="5,0"/> <TextBox x:Name="textBox3" Margin="5"/> <TextBox x:Name="textBox4" Margin="5,0"/> </StackPanel> </Border> </UserControl> namespace WpfApplication1_2 { /// <summary> /// MiniView.xaml 的交互逻辑 /// </summary> //自定义命令目标 public partial class MiniView : UserControl,IView { public MiniView() { InitializeComponent(); } //继承自IView的成员们 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(); } } }

程序的流程图如下:

最新回复(0)