WPF学习笔记(24)命令与ICommand
命令与ICommand
- 一、命令
- 1. ICommandSource
- 2. 示例
- 3. CommandBinding
- 二、ICommand
- 1.ICommand接口
- 2. ICommand用法
- 3. CanExecute
- 总结
一、命令
官方文档:https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/advanced/commanding-overview
1. ICommandSource
官方文档:https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/advanced/how-to-implement-icommandsource
2. 示例
<Grid><TextBox x:Name="textBox" Text="TextBox" VerticalAlignment="Top" Height="150" Margin="219,35,187,0"/><Button x:Name="buttonCut"Content="剪切"Command="ApplicationCommands.Cut"CommandTarget="{Binding ElementName=textBox}"Height="100" Margin="219,233,419,102"/><Button x:Name="buttonPaste"Content="粘贴"Command="ApplicationCommands.Paste"CommandTarget="{Binding ElementName=textBox}" Margin="453,233,187,107"/></Grid>
3. CommandBinding
- 当TEXTBOX内容长度大于0时,CanExecute被判断为true
- true可以启用命令源按钮,false禁用按钮
<Window.CommandBindings><CommandBinding Command="ApplicationCommands.Cut" CanExecute="my_CanExecute" Executed="my_Execute"/>
</Window.CommandBindings><Grid><TextBox x:Name="textBox" Text="TextBox" VerticalAlignment="Top" Height="150" Margin="219,35,187,0"/><Button x:Name="buttonCut" Margin="219,233,419,102" Height="100" Content="剪切"Command="ApplicationCommands.Cut"/><Button x:Name="buttonPaste"Content="粘贴"Command="ApplicationCommands.Paste"CommandTarget="{Binding ElementName=textBox}"Margin="453,233,187,107"/>
</Grid>
public partial class MainWindow : Window
{public MainWindow(){InitializeComponent();}private void my_Execute(object sender, ExecutedRoutedEventArgs e){Console.WriteLine("Execute!!!");textBox.Cut();}private void my_CanExecute(object sender, CanExecuteRoutedEventArgs e){e.CanExecute = textBox.SelectionLength > 0;}
}
二、ICommand
官方文档:https://learn.microsoft.com/zh-cn/dotnet/api/system.windows.input.icommand?view=net-9.0
1.ICommand接口
ICommand接口的属性与事件如下:
2. ICommand用法
<Grid><TextBox HorizontalAlignment="Left" Margin="222,62,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="422" Height="102"/><Button x:Name="button1" Content=" 自定义命令" HorizontalAlignment="Left" Margin="222,217,0,0" VerticalAlignment="Top" Height="64" Width="145"/><Button x:Name="button2" Content="触发事件" HorizontalAlignment="Left" Margin="499,217,0,0" VerticalAlignment="Top" Height="64" Width="145"/></Grid>
在MainWindow.xaml.cs文件内自定义命令接口
<Window.Resources><local:MyCommand x:Key="MyCMD" />
</Window.Resources>
<Grid><TextBox x:Name="textBox" HorizontalAlignment="Left" Margin="222,62,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="422" Height="102"/><Button x:Name="button1" Content=" 自定义命令"Command="{StaticResource MyCMD}"CommandParameter="{Binding ElementName=textBox,Path=Text}"HorizontalAlignment="Left" Margin="222,217,0,0" VerticalAlignment="Top" Height="64" Width="145"/><Button x:Name="button2" Content="触发事件" HorizontalAlignment="Left" Margin="499,217,0,0" VerticalAlignment="Top" Height="64" Width="145"/>
</Grid>
public class MyCommand : ICommand
{public event EventHandler CanExecuteChanged;public bool CanExecute(object parameter){return true;//按钮可以点击//return false;//按钮不可点击}//Execute方法中实现命令处理逻辑public void Execute(object parameter){MessageBox.Show(parameter.ToString());}
}
以下为CanExecuteChanged的事件解释:
public partial class MainWindow : Window
{public MainWindow(){InitializeComponent();}private void button2_Click(object sender, RoutedEventArgs e){MyCommand c = Resources["MyCMD"] as MyCommand;//触发CanExecuteChangedc.RaiseCanExecuteChanged();}
}
//实现ICommand接口
public class MyCommand : ICommand
{public event EventHandler CanExecuteChanged;public bool CanExecute(object parameter){String str = parameter as String;return str?.Length > 0;//return true;//按钮可以点击//return false;//按钮不可点击}//Execute方法中实现命令处理逻辑public void Execute(object parameter){MessageBox.Show(parameter.ToString());}//定义一个方法,手动触发CanExecuteChanged事件public void RaiseCanExecuteChanged() {//表面上,没有为 CanExecuteChanged 这个事件添加任何订阅方法//例如CanExecuteChanged += fun;//但是我们为按钮设置命令时,自动加入了一个此事件订阅的方法,//并且这个订阅的方法,会去调用命令的CanExecute//可通过Delegate查看CanExecuteChanged的来源与内容Delegate[] delegates = CanExecuteChanged.GetInvocationList();//delegates内查看到 System.Windows.Input.CanExecuteChangedEventManager+HandlerSink"//OnCanExecuteChangedCanExecuteChanged?.Invoke(this, EventArgs.Empty);}
3. CanExecute
以ButtonBase为例介绍,调用了OnCommandChanged
OnCommandChanged调用了HookCommand
HookCommand调用了AddHandler,CanExecute调用了CanExecuteCommandSource
AddHandler调用的PrivateAddHandler又new 了一个HandlerSink
HandlerSink将OnCommandChanged函数添加到Icommand接口的CanExecuteChanged
总结
关于ButtonBase、CanExecuteChangedEventManager、commandHelpers的详细原理、我们可以参考WPF框架的源码
https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/Primitives/ButtonBase.cs
https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/CanExecuteRoutedEventArgs.cs
https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Commands/CommandHelpers.cs