命令系统的基本元素和关系
WPF命令系统的组成要素:
A.命令(command):WPF命令实际上就是实习了ICommand接口的类。平时使用最多的就是RoutedCommand类。
B.命令源(command source):即命令的发送者,实现了ICommandSource接口的类。
C.命令目标(command Target):即命令发给了谁或理解为命令的接收者。命令目标必须是实现了IInputElement接口的类。
D.命令关联(command Binding):负责把一些外围逻辑和命令关联起来。比如执行之前对命令是否可以执行进行判、命令执行之后还有哪些后续工作等。
命令使用的步骤:
1.创建命令类
2.声明命名实例
3.指定命令源
4.指定命令目标
5.设置命令关联
ICommand接口与RoutedCommand
WPF中的命令是实现了ICommand接口的类。
ICommand接口非常简单,只包含两个方法一个事件。
<1>Execute方法:命令执行,或者说命令执行于命令目标之上。需要注意的是,现实世界中的命令是不会自己执行的,而这里,执行变成了命令的方法,有点拟人化的味道。
<2>CanExecute方法:在执行之前探知命令是否可以执行。
<3>CanExecuteChanged事件:当命令的可执行状态改变的时候,可激发此事件通知其它对象。
RoutedCommand就是一个实现了ICommand接口的类。
RoutedCommand在实现ICommand接口时,并未向Execute和CanExecute方法中添加任何逻辑,
也就是说,它是通用的、与具体的业务逻辑无关的。
<Window x:Class="命令1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <StackPanel Background="Red" x:Name="sp1"> <Button x:Name="btn1" Content="Send Clear Command" Margin="5" Background="{Binding}"/> <TextBox x:Name="txtA" Margin="5,0" Height="200"/> </StackPanel> </Window>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | using System.Windows; using System.Windows.Input; namespace 命令1 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); InitializeCommand(); } //1.创建命令类 //2.声明命名实例 //3.指定命令源—— 命令发送者 //4.指定命令目标——命令接收者 //5.设置命令关联 //声明并定义命令 private RoutedCommand RouutedCommand = new RoutedCommand( "可输入非空字符" , typeof (MainWindow)); private void InitializeCommand() { //把命令赋值给命令源,并定义快捷键 this .btn1.Command = RouutedCommand; this .RouutedCommand.InputGestures.Add( new KeyGesture(Key.C, ModifierKeys.Alt)); //指定命令目标 this .btn1.CommandTarget = txtA; //创建命令关联 CommandBinding commandBinding = new CommandBinding(); commandBinding.Command = RouutedCommand; //只关注与rouutedCommand相关的命令 commandBinding.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute); commandBinding.Executed += new ExecutedRoutedEventHandler(cb_Execute); //把命令关联安置在外围控件上 this .sp1.CommandBindings.Add(commandBinding); } //当命令到达目标之后,此方法被调用 private void cb_Execute( object sender, ExecutedRoutedEventArgs e) { this .txtA.Clear(); //避免事件继续向上传递而降低程序性能 e.Handled = true ; } //当探测命令是否可执行的时候该方法会被调用 private void cb_CanExecute( object sender, CanExecuteRoutedEventArgs e) { if ( string .IsNullOrEmpty(txtA.Text)) { e.CanExecute = false ; } else { e.CanExecute = true ; } //避免事件继续向上传递而降低程序性能 e.Handled = true ; } } } //对于以上的代码,需要注意以下几点: //第一,使用命令可以避免自己写代码判断Button是否可以用以及添加快捷键。 //第二,RountedCommand是一个与业务逻辑无关的类,只负责在程序中跑腿而并不对命令目标进行操作, //TextBox并不是由它清空的。那么TextBox的情况操作是谁呢?答案是CommandBinding。 //因为无论是探测命令是否可以执行还是命令送达目标,都会激发命令目标发送路由事件, //这些事件会沿着UI元素树向上传递,最终被CommandBinding所捕捉。 //本例中CommandBinding被安装在外围的StackPanel上,Commandbinding站在高处起一个侦听器的作用, //而且专门针对rouutedCommand命令捕捉与其相关的事件。 //本例中,当CommandBinding捕捉到CanExecute就会调用cb_CanExecute方法。 //当捕捉到是Executed的时候,就调用cb_Execute事件。 //第三,因为CanExecute事件的激发频率比较高,为了避免降低性能,在处理完毕之后建议将e.Handle设置为true。 //第四,CommandBinding一定要设置在命令目标的外围控件上,不然无法捕捉CanExecute和Executed等路由事件。 |
实例二:
<Window x:Class="命令2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <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 HorizontalAlignment="Left" Name="textBlock1" Text="Name:" VerticalAlignment="Center" Grid.Row="0"/> <TextBox x:Name="txtName" Margin="60,5,0,0" Grid.Row="0"/> <Button Content="New Teacher" Grid.Row="2" Command="New" CommandParameter="Teacher"/> <Button Content="New Student" Grid.Row="4" Command="New" CommandParameter="Student"/> <ListBox Grid.Row="6" x:Name="lbInfos"/> </Grid> <!--为窗体添加CommandBinding--> <Window.CommandBindings> <CommandBinding Command="New" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/> </Window.CommandBindings> </Window>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | using System.Windows; using System.Windows.Input; namespace 命令2 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { //两个按钮都使用的是New命令,但分别使用的是Student和Teacher做为的参数。 public MainWindow() { InitializeComponent(); } /// <summary> /// 当探测命令是否可执行的时候该方法会被调用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void CommandBinding_CanExecute( object sender, CanExecuteRoutedEventArgs e) { if ( string .IsNullOrEmpty(txtName.Text)) { e.CanExecute = false ; } else { e.CanExecute = true ; } //路由终止,提高系统性能 e.Handled = true ; } /// <summary> /// 当命令到达目标之后,此方法被调用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void CommandBinding_Executed( object sender, ExecutedRoutedEventArgs e) { if (e.Parameter.ToString() == "Student" ) { this .lbInfos.Items.Add( string .Format( "New Student:{0} 好好学习,天天向上。" , txtName.Text)); } else if (e.Parameter.ToString() == "Teacher" ) { this .lbInfos.Items.Add( string .Format( "New Teacher:{0} 学而不厌,诲人不倦。" , txtName.Text)); } //路由终止,提高系统性能 e.Handled = true ; } } } |
实例三:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 命令4 { public interface IView { //属性 bool IsChanged { get ; set ; } //方法 void SetBinding(); void Refresh(); void Clear(); void Save(); } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Input; namespace 命令4 { 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(); } } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Controls; using System.Windows.Input; using System.Windows; namespace 命令4 { public class MyCommandSource : UserControl, ICommandSource { /// <summary> /// 继承自ICommand的3个属性 /// </summary> public ICommand Command { get ; set ; } public object CommandParameter { get ; set ; } public IInputElement CommandTarget { get ; set ; } //在命令目标上执行命令,或者说让命令作用于命令目标 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base .OnMouseLeftButtonDown(e); if ( this .CommandTarget != null ) { this .Command.Execute(CommandTarget); } } } } |
<UserControl x:Class="命令4.UCMniView" 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="300" d:DesignWidth="300"> <Border CornerRadius="5" BorderBrush="GreenYellow" BorderThickness="2"> <StackPanel> <TextBox Margin="5" x:Name="txt1"></TextBox> <TextBox Margin="5" x:Name="txt2"></TextBox> <TextBox Margin="5" x:Name="txt3"></TextBox> <TextBox Margin="5" x:Name="txt4"></TextBox> </StackPanel> </Border> </UserControl>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | using System; using System.Windows.Controls; namespace 命令4 { /// <summary> /// UserControl.xaml 的交互逻辑 /// </summary> public partial class UCMniView : UserControl,IView { public UCMniView() { InitializeComponent(); } public bool IsChanged { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public void SetBinding() { throw new NotImplementedException(); } public void Refresh() { throw new NotImplementedException(); } public void Clear() { this .txt1.Clear(); this .txt2.Clear(); this .txt3.Clear(); this .txt4.Clear(); } public void Save() { throw new NotImplementedException(); } } } |
<Window x:Class="命令4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:命令4" Title="MainWindow" Height="350" Width="525"> <StackPanel> <local:MyCommandSource x:Name="myCommandSource1"> <TextBlock Text="清除" Width="80" FontSize="16" TextAlignment="Center" Background="LightGreen"/> </local:MyCommandSource> <local:UCMniView x:Name="mniView1" /> </StackPanel> </Window>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace 命令4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); ClearCommand clearCommand = new ClearCommand(); this .myCommandSource1.Command = clearCommand; this .myCommandSource1.CommandTarget = mniView1; } } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本