命令與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