1、概述
1.1 WPF C# 命令的本質
命令是 WPF 中的輸入機制,它提供的輸入處理比設備輸入具有更高的語義級別。 例如,在許多應用程序中都能找到的“復制”、“剪切”和“粘貼”操作就是命令。
WPF 中的命令是通過實現 ICommand 接口創建的。 ICommand 的 WPF 實現是 RoutedCommand 類,這是WPF C# 命令的本質。
1.2 WPF C# 命令的機制
1.2.1 編程范圍
ICommand 公開兩個方法(Execute 及 CanExecute)和一個事件(CanExecuteChanged)。
Execute 執行與命令關聯的操作。
CanExecute 確定是否可以在當前命令目標上執行命令。
如果集中管理命令操作的命令管理器檢測到命令源中發生了更改,此更改可能使得已引發但尚未由命令綁定執行的命令無效,則將引發 CanExecuteChanged。
1.2.2 輸入源
WPF 中的主要輸入源是鼠標、鍵盤、墨跡和路由命令。 更加面向設備的輸入使用路由事件( RoutedEvent )來通知應用程序頁中的對象已發生了輸入事件。無論何種輸入源, RoutedCommand 沒有不同。
1.2.3 應用程序邏輯
RoutedCommand 的 Execute 和 CanExecute 方法不包含命令的應用程序邏輯,而是引發這樣的路由事件:沿元素樹以隧道和冒泡形式傳遞,直到遇到具有 CommandBinding 的對象。CommandBinding 包含這些事件的處理程序,執行此命令的就是這些處理程序。( 有關 WPF 中的事件路由的更多信息,請參見路由事件概述。)
1.2.4 命令引發路由事件
RoutedCommand 上的 Execute 方法在命令目標上引發 PreviewExecuted 和 Executed 事件。
RoutedCommand 上的 CanExecute 方法在命令目標上引發 CanExecute 和PreviewCanExecute 事件。
這些事件沿元素樹以隧道和冒泡形式傳遞,直到遇到具有該特定命令的 CommandBinding 的對象。
1.3、WPF C# 命令分類
WPF 通過命令類庫提供一組預定義命令。如果命令庫類中的命令不滿足您的需要,則您可以創建自己的命令。
1.3.1 內置命令庫
WPF 提供一組預定義命令庫。 命令庫包含以下類:ApplicationCommands、NavigationCommands、MediaCommands、EditingCommands 以及 ComponentCommands。
這些類提供了一組常用的路由命令.諸如 Cut、BrowseBack、BrowseForward、Play、Stop 和 Pause 等命令。
這些命令僅包含 RoutedCommand 對象,而不包含命令的實現邏輯。 實現邏輯由在其上執行命令的對象負責。
命令庫中的許多命令都包括一組默認輸入綁定。 例如,如果您指定應用程序處理復制命令,則會自動獲得鍵盤綁定“Ctrl+C”。您還會獲得其他輸入設備(如 Tablet PC 鋼筆筆勢和語音信息)的綁定。
當您使用 XAML 引用不同的命令庫中的命令時,通常可以忽略公開靜態命令屬性的庫類的類名。 通常,命令名稱作為字符串是明確的,並且存在用於提供命令的邏輯分組的所屬類型,但是這些類型對於消除歧義並不是必需的。 例如,您可以指定 Command="Cut",而不必指定更詳細的 Command="ApplicationCommands.Cut"。 這是一種內置於命令的 WPF XAML 處理器中的便利機制(更准確地說,它是 WPF XAML 處理器在加載時引用的 ICommand 的類型轉換器行為)。
1.3.2 創建自定義命令
如果命令庫類中的命令不滿足您的需要,則您可以創建自己的命令。
有兩種方法可創建自定義命令。 第一種是從頭開始,並實現 ICommand 接口。 另一種方法,也是更常用的方法,是創建RoutedCommand 或 RoutedUICommand。
有關創建自定義 RoutedCommand 的示例,請參見 Create a Custom RoutedCommand Sample(創建自定義 RoutedCommand 示例)。
1.4、WPF C# 命令的用途
命令有若干用途。
1.4.1 將語義以及調用命令的對象與執行命令的邏輯分離
第一個用途是將語義以及調用命令的對象與執行命令的邏輯分離開來。 這使得多個完全不同的源可以調用相同的命令邏輯,並使得可以針對不同的目標對命令邏輯進行自定義。 例如,在許多應用程序中都能找到的編輯操作“復制”、“剪切”和“粘貼”都可使用不同的用戶操作進行調用(如果這些操作是使用命令實現的)。 應用程序可能允許用戶通過單擊按鈕、選擇菜單項或使用組合鍵(例如 Ctrl+X)剪切所選的對象或文本。 通過使用命令,您可以將各種類型的用戶操作綁定到同一邏輯。
1.4.2 操作是否可用
命令的另一個用途是指示操作是否可用。 仍然以剪切對象或文本作為示例,該操作只有在選擇了某些內容時才有意義。 如果用戶嘗試在沒有選擇任何內容的情況下剪切對象或文本,則不會發生任何操作。 為了向用戶指明這一點,許多應用程序都會禁用按鈕和菜單項,以使用戶了解是否能夠執行某項操作。 命令可通過實現 CanExecute 方法來指出操作是否可以執行。 按鈕可以訂閱CanExecuteChanged 事件,並且,如果 CanExecute 返回 false,則可以禁用按鈕,或者,如果 CanExecute 返回 true,則可以啟用按鈕。
1.4.3 根據目標的類型采取相應的操作
命令的語義在應用程序和類之間可能是一致的,但是操作的邏輯是所作用於的特定對象所特有的。 Ctrl+X 組合鍵在文本類、圖像類以及 Web 瀏覽器中調用“剪切”命令,但用於執行“剪切”操作的實際邏輯是由執行剪切的應用程序定義的。 RoutedCommand 使客戶端能夠實現邏輯。 文本對象可以將所選文本剪切到剪貼板中,而圖像對象則可以剪切所選圖像。 當應用程序處理Executed 事件時,它將能訪問命令的目標,並根據目標的類型采取相應的操作。
2、使用WPF 命令
2.1 WPF 命令中的四個主要概念
WPF 中的路由命令模型可以分為四個主要概念:命令、命令源、命令目標以及命令綁定:
-
命令:是要執行的操作。
-
命令源:是調用命令的對象。
-
命令目標:是在其上執行命令的對象。
-
命令綁定:是將命令邏輯映射到命令的對象。
參見下面的示例。其中,Paste 命令是命令,MenuItem 是命令源,TextBox 是命令目標,命令綁定由 TextBox 控件提供。
值得注意的是,CommandBinding 並不總是由充當命令目標類的控件提供。 非常常見的情況是,CommandBinding 必須由應用程序開發人員創建,或者 CommandBinding 可能附加到命令目標的上級。
2.2 WPF 中的簡單命令示例
在 WPF 中使用命令的最簡單方法是從某個命令庫類中使用預定義的 RoutedCommand;使用對處理此命令具有固有支持的控件;以及使用對調用命令具有固有支持的控件。
Paste 命令是ApplicationCommands 類中的預定義命令之一。 TextBox 控件具有處理 Paste 命令的內置邏輯。 MenuItem 類具有對調用命令的固有支持。下面的示例演示如何設置 MenuItem,以便單擊該菜單項時,將對 TextBox 調用 Paste 命令(假定該 TextBox 具有鍵盤焦點)。
XAML <StackPanel> <Menu> <MenuItem Command="ApplicationCommands.Paste" /> </Menu> <TextBox /> </StackPanel>
C# // Creating the UI objects StackPanel mainStackPanel = new StackPanel(); TextBox pasteTextBox = new TextBox(); Menu stackPanelMenu = new Menu(); MenuItem pasteMenuItem = new MenuItem(); // Adding objects to the panel and the menu stackPanelMenu.Items.Add(pasteMenuItem); mainStackPanel.Children.Add(stackPanelMenu); mainStackPanel.Children.Add(pasteTextBox); // Setting the command to the Paste command pasteMenuItem.Command = ApplicationCommands.Paste; // Setting the command target to the TextBox pasteMenuItem.CommandTarget = pasteTextBox;
2.3 編程設計
2.3.1 命令
如前述所述,WPF 中的命令是通過實現 ICommand 接口創建的。 ICommand 公開兩個方法(Execute 及 CanExecute)和一個事件(CanExecuteChanged)。 Execute 執行與命令關聯的操作。CanExecute 確定是否可以在當前命令目標上執行命令。 如果集中管理命令操作的命令管理器檢測到命令源中發生了更改,此更改可能使得已引發但尚未由命令綁定執行的命令無效,則將引發 CanExecuteChanged。
RoutedCommand 上的 Execute 方法在命令目標上引發 PreviewExecuted 和 Executed 事件。 RoutedCommand 上的 CanExecute 方法在命令目標上引發 CanExecute 和PreviewCanExecute 事件。 這些事件沿元素樹以隧道和冒泡形式傳遞,直到遇到具有該特定命令的 CommandBinding 的對象。
2.3.2 命令源
命令源是調用命令的對象。 例如,MenuItem、Button 和 KeyGesture 就是命令源。
WPF 中的命令源通常實現 ICommandSource 接口。
ICommandSource 公開三個屬性:Command、CommandTarget 和 CommandParameter:
-
Command 是在調用命令源時執行的命令。
-
CommandTarget 是要在其上執行命令的對象。 值得注意的是,在 WPF 中,ICommandSource 上的 CommandTarget 屬性只有在 ICommand 是 RoutedCommand 時才適用。 如果在 ICommandSource 上設置了 CommandTarget,而對應的命令不是 RoutedCommand,將會忽略命令目標。 如果未設置 CommandTarget,則具有鍵盤焦點的元素將是命令目標。
-
CommandParameter 是用戶定義的數據類型,用於將信息傳遞到實現命令的處理程序。
實現 ICommandSource 的 WPF 類包括:ButtonBase、MenuItem、Hyperlink 以及 InputBinding。
ButtonBase 、MenuItem 和 Hyperlink 在被單擊時調用命令,InputBinding 在與之關聯的 InputGesture 執行時調用命令。
2.3.2.1 舉例: ContextMenu 中的 MenuItem 用作 Properties 命令的命令源
下面的示例演示如何將 ContextMenu 中的 MenuItem 用作 Properties 命令的命令源。
XAML <StackPanel> <StackPanel.ContextMenu> <ContextMenu> <MenuItem Command="ApplicationCommands.Properties" /> </ContextMenu> </StackPanel.ContextMenu> </StackPanel>
C# StackPanel cmdSourcePanel = new StackPanel(); ContextMenu cmdSourceContextMenu = new ContextMenu(); MenuItem cmdSourceMenuItem = new MenuItem(); // Add ContextMenu to the StackPanel. cmdSourcePanel.ContextMenu = cmdSourceContextMenu; cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem); // Associate Command with MenuItem. cmdSourceMenuItem.Command = ApplicationCommands.Properties;
通常,命令源會偵聽 CanExecuteChanged 事件。 此事件通知命令源,在當前命令目標上執行命令的能力可能已更改。 命令源可以通過使用 CanExecute 方法來查詢 RoutedCommand 的當前狀態。 之后,如果命令無法執行,則命令源可以禁用其自身。 這種情況的一個示例是:當無法執行命令時,MenuItem 會將自己顯示為灰色。
2.3.2.2 舉例: InputGesture 作命令源
可以將 InputGesture 用作命令源。 WPF 中兩種類型的輸入筆勢是 KeyGesture 和 MouseGesture。 可以將 KeyGesture 視為鍵盤快捷方式,如 Ctrl+C。 KeyGesture 由一個 Key 和一組ModifierKeys 組成。 MouseGesture 由一個 MouseAction 和一組可選的 ModifierKeys 組成。
為了使 InputGesture 充當命令源,必須將它與一個命令關聯。 有幾種方法可實現此目的。 一種方法是使用 InputBinding。
下面的示例演示如何在 KeyGesture 與 RoutedCommand 之間創建一個 KeyBinding。
XAML <Window.InputBindings> <KeyBinding Key="B" Modifiers="Control" Command="ApplicationCommands.Open" /> </Window.InputBindings>
C# KeyGesture OpenKeyGesture = new KeyGesture( Key.B, ModifierKeys.Control); KeyBinding OpenCmdKeybinding = new KeyBinding( ApplicationCommands.Open, OpenKeyGesture); this.InputBindings.Add(OpenCmdKeybinding);
2.3.2.3 InputGesture 與 RoutedCommand 關聯
將 InputGesture 與 RoutedCommand 關聯的另一種方法是將 InputGesture 添加到 RoutedCommand 的 InputGestureCollection。
下面的示例演示如何將一個 KeyGesture 添加到 RoutedCommand 的 InputGestureCollection。
WPF C# 命令的運行機制
命令源
命令源是調用命令的對象。 例如,MenuItem、Button 和 KeyGesture 就是命令源。
WPF 中的命令源通常實現 ICommandSource 接口。
ICommandSource 公開三個屬性:Command、CommandTarget 和 CommandParameter:
-
Command 是在調用命令源時執行的命令。
-
CommandTarget 是要在其上執行命令的對象。 值得注意的是,在 WPF 中,ICommandSource 上的 CommandTarget 屬性只有在 ICommand 是 RoutedCommand 時才適用。 如果在 ICommandSource 上設置了 CommandTarget,而對應的命令不是 RoutedCommand,將會忽略命令目標。 如果未設置 CommandTarget,則具有鍵盤焦點的元素將是命令目標。
-
CommandParameter 是用戶定義的數據類型,用於將信息傳遞到實現命令的處理程序。
實現 ICommandSource 的 WPF 類包括:ButtonBase、MenuItem、Hyperlink 以及 InputBinding。 ButtonBase 、MenuItem 和 Hyperlink 在被單擊時調用命令,InputBinding 在與之關聯的 InputGesture 執行時調用命令。