1. 程式人生 > >WPF 之命令(七)

WPF 之命令(七)

# 一、前言 ​ 事件的作用是釋出和傳播一些訊息,訊息送達接收者,事件的使命也就完成了,至於訊息響應者如何處理髮送來的訊息並不做規定,每個接收者可以使用自己的行為來響應事件。即事件不具有約束力。 ​ 命令就有約束力,不僅可以約束程式碼,還可以約束步驟邏輯。 # 二、WPF 的 命令系統 ​ WPF 中,命令系統由以下元素構成: 1. 命令(Command):實現 ICommand 介面。表示一個程式任務,並可跟蹤該任務是否完成。 2. 命令源(Commannd Source):命令的傳送者。需實現 ICommandSource 介面。 3. 命令目標(Command Target):命令的接收者。需實現 IInputElment 介面的類。 4. 命令關聯(Command Binding):負責把外圍邏輯和命令關聯起來。 ![image-20210208165152323](https://i.loli.net/2021/02/08/nEBSZHTjvsK4wM1.png) ​  WPF中提供了一組已定義命令,命令包括以下類:[ApplicationCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.input.applicationcommands(v=vs.110).aspx)、[NavigationCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.input.navigationcommands(v=vs.110).aspx)、[MediaCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.input.mediacommands(v=vs.110).aspx)、[EditingCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.documents.editingcommands(v=vs.110).aspx) 以及[ComponentCommands](http://msdn.microsoft.com/zh-cn/library/system.windows.input.componentcommands(v=vs.110).aspx)。 這些類提供諸如 [Cut](http://msdn.microsoft.com/zh-cn/library/system.windows.input.applicationcommands.cut(v=vs.110).aspx)、[BrowseBack](http://msdn.microsoft.com/zh-cn/library/system.windows.input.navigationcommands.browseback(v=vs.110).aspx)、[BrowseForward](http://msdn.microsoft.com/zh-cn/library/system.windows.input.navigationcommands.browseforward(v=vs.110).aspx)、[Play](http://msdn.microsoft.com/zh-cn/library/system.windows.input.mediacommands.play(v=vs.110).aspx)、[Stop](http://msdn.microsoft.com/zh-cn/library/system.windows.input.mediacommands.stop(v=vs.110).aspx) 和 [Pause](http://msdn.microsoft.com/zh-cn/library/system.windows.input.mediacommands.pause(v=vs.110).aspx) 等命令。下面我們使用 ApplicationCommands 進行以下測試: ```c# // 命令執行具體操作 private void CommandBinding_OnExecuted(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show($"New 命令被觸發了,命令源是:{e.Source}"); } ``` ```html ``` ​ 點選按鈕後,輸出結果為: ``` New 命令被觸發了,命令源是:System.Windows.Controls.Button: New ``` # 三、自定義命令 ​ 例如,我們需要增加一個數據的查庫與上傳資料操作,那麼需要實現 Query 和 Insert 命令,為了能夠直接供 UI 使用,我們宣告 RoutedUICommand 命令,具體如下: ```c# public class DatabaseCommands { private static RoutedUICommand _query; private static RoutedUICommand _insert; static DatabaseCommands() { InputGestureCollection inputs = new InputGestureCollection(); inputs.Add(new KeyGesture(Key.Q ,ModifierKeys.Control, "Ctrl+R")); _query = new RoutedUICommand( "Query", "Query", typeof(DatabaseCommands), inputs); inputs.Add(new KeyGesture(Key.D, ModifierKeys.Control, "Ctrl+D")); _insert = new RoutedUICommand( "Add", "Add", typeof(DatabaseCommands), inputs); } public static RoutedUICommand Query { get { return _query; } } public static RoutedUICommand Insert { get { return _insert; } } } ``` ​ 命令實現與繫結到 UI 上的操作如下: ```c# // 命令執行具體操作 private void DatabaseCommandsQuery_OnExecuted(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show($"Query 命令被觸發了,命令源是:{e.Source}"); } private void DatabaseCommandsAdd_OnExecuted(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show($"Add 命令被觸發了,命令源是:{e.Source}"); } ``` ```
``` ​ 當我們點選 Query 和 Insert 操作時,分別執行對應的操作。上述例子中,Command 的執行程式碼是在 XAML 中宣告的,那我們把命令的執行程式碼進行程式碼後置,且為了滿足不同的條件,我們宣告一個 RelayCommand 基類進行封裝,具體如下: ```c# /// /// The base implementation of a command. /// abstract class CommandBase : ICommand { /// /// Occurs when changes occur that affect whether or not the command should execute. ///
public event EventHandler CanExecuteChanged { add { System.Windows.Input.CommandManager.RequerySuggested += value; } remove { System.Windows.Input.CommandManager.RequerySuggested -= value; } } /// /// Raises the event. /// public void OnCanExecuteChanged() { System.Windows.Input.CommandManager.InvalidateRequerySuggested(); } /// /// Defines the method that determines whether the command can execute in its current state. ///
/// Data used by the command. If the command does not require data to be passed, this object can be set to null. /// /// true if this command can be executed; otherwise, false. /// public virtual bool CanExecute(object parameter) { return true; } /// /// Defines the method to be called when the command is invoked. /// /// Data used by the command. If the command does not require data to be passed, this object can be set to null. public void Execute(object parameter) { if (!CanExecute(parameter)) { return; } OnExecute(parameter); } /// /// Executes the command. /// /// The parameter. protected abstract void OnExecute(object parameter); } /// /// The command that relays its functionality by invoking delegates. /// class RelayCommand : CommandBase { private readonly Action _execute; private readonly Func _canExecute; /// /// Initializes a new instance of the class. /// /// The execute. /// The can execute. public RelayCommand(Action execute, Func canExecute = null) { if (canExecute == null) { // no can execute provided, then always executable canExecute = (o) => true; } this._execute = execute; this._canExecute = canExecute; } /// /// Defines the method that determines whether the command can execute in its current state. /// /// Data used by the command. If the command does not require data to be passed, this object can be set to null. /// /// true if this command can be executed; otherwise, false. /// public override bool CanExecute(object parameter) { return _canExecute == null || _canExecute.Invoke(parameter); } /// /// Executes the command. /// /// The parameter. protected override void OnExecute(object parameter) { _execute?.Invoke(parameter); } } ``` ​ 然後,我們實現一個示例,點選 Clear 按鈕時,清空 TextBox 中的所有內容,那麼需要宣告一個 Model 和 ViewModel ,具體實現可以閱讀 [WPF 之 INotifyPropertyChanged 介面的使用 (一)](https://www.cnblogs.com/dongweian/p/14361912.html): ```c# class Student { public string Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Phone { get; set; } } class MainWindowVM : NotifyProperty { private Student _student; public Student Student { get => _student; set => SetProperty(ref _student, value); } public ICommand Clear { get; set; } private void ClearCommand(object args) { Student = new Student(); } public MainWindowVM() { _student = new Student() { Id = "01", Name = "Dwayne", Email = "[email protected]", Phone = "180888888888", }; Clear=new RelayCommand(ClearCommand,null); } } ``` ​ 最後,我們把該 ViewModel 繫結到 XAML 上,具體如下: ``` ``` ​ 當我們點選 Clear 按鈕時,所有的 TextBox 就被清空了顯示。其實,上述這個例子就是一個簡單的 **MVVM** 示例,它讓邏輯程式碼和 UI 完全分離。對於 Command 來說,當我們要執行 TextChanged 事件時,需要新增 “System.Windows.Interactivity” ,然後在 XAML 中新增如下引用: ```html xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" ``` ```html ``` 當我們改變該文字框的內容時,也可執行 Clear