理解命令
傳統的設計在需要地方添加事件處理程序,並使用各個事件處理程序調用恰當的應用程序方法。如下圖,許多應用程序任務可通過各種不同的路由觸發,所以經常編寫多個事件處理程序來調用相同的應用程序方法。本身這並不是什么問題。但當需要處理用戶界面狀態時,問題就變復雜了。如果要禁用PrintDocumnt()任務。需要禁用兩個菜單命令和一個工具欄按鈕,使它們不能被單擊並且禁用Ctrl+P快捷鍵。編寫代碼完成這些工作很麻煩。
WPF使用新的命令模型解決這個問題。它增加了兩個重要特性。
- 將事件委托到適當的命令。
- 使控件的啟用狀態和相應命令的狀態保持同步。
WPF命令模型
WPF命令模型具有如下4個重要元素
- 命令:命令表示應用程序任務,並且跟蹤任務是否能被執行。然而,命令實際上不包含執行應用程序任務的代碼。
- 命令綁定:每個命令綁定針對用戶界面的具體區域,將命令連接到相關的應用程序邏輯,這種分解的設計是非常重要的,因為每個命令可用於應用程序的多個地方, 並且每個地方具有不同的意義。為處理這一問題,需要同一命令與不同的命令綁定。
- 命令源:命令源觸發命令.例如:MenuItem和Button都是命令源。單擊他們會執行綁定命令。
- 命令目標:命令目標是在其中執行命令的元素。例如,Paste命令可在TextBox控件中插入文本,而OpenFile命令可在DocumentViewer中打開文檔。
ICommand接口
WPF命令模型的核心是System.Windows.Input.ICommand接口。
public interface ICommand { void Execute(object parameter); bool CanExecute(object parameter); event EventHandler CanExecuteChanged; }
-
Execute()方法將包含應用程序任務邏輯。
-
CanExecute()方法返回命令的狀態。
-
通過CanExecuteChanged事件,當命令可用時,命令源可自動啟用自身,當命令不可用時,禁用自身。
RoutedCommand類
RoutedCommand類是WPF中唯一實現了ICommand接口的類。所有WPF命令都是RoutedCommand類及其派生類的實例。
RoutedUICommand類
RoutedUICommand類繼承自RoutedCommand類,WPF提供的所有預先構建好的命令都是RoutedUICommand對象。
命令庫
WPF提供了基本命令庫,基本命令庫中保存的命令超過100條,這些命令通過以下5個專門的靜態類的靜態屬性控制。
-
ApplicationCommands:該類提供了通用命令。包括剪切板命令(如Copy、Cut、Paste)以及文檔命令(如New、Open、Save、Save As和Print)
-
NavigationCommands:該類提供了用於導航的命令,包括基於頁面的應用程序設計的一些命令,如BrowseBack、BrowseForward和NextPage。
-
EditingCommands:該類提供了許多重要的文檔編輯命令。包含用於移動的命令,MoveToLineEnd、MoveLeftByWord和MoveUpByPage,選擇內容命令SelectToLineEnd、SelectLeftByWord。
-
ComponentCommands:改類提供了由用戶界面組件使用的命令,包括用於移動和選擇內容的命令,這些命令和EditingCommand類中的一些命令類似。
-
MediaCommands:該類提供了一組用於處理多媒體的命令,如Play、Pause、NextTrack以及IncreaseVolume。
命令源
命令庫中的命令始終可用。觸發他們最簡單的方法是將他們關聯到實現了ICommandSource接口的控件,其中包括繼承自ButtonBase類的控件,Button和CheckBox、ListBoxItem、HypeLink以及MenuItem。
ICommandSource接口定義了三個屬性
-
Command 指向連接的指令。
-
CommandParameter 提供其他希望隨命令發送的數據。
-
CommandTarget確定將在其中執行命令的元素。
使用Command屬性連接到ApplicationCommands.New
<Button Command="ApplicationCommands.New">New</Button>
命令綁定
當命令關聯到命令源時,命令源將會被自動禁用。如上按鈕,查詢了命令的狀態,而且由於命令還沒有與其關聯的綁定,所以按鈕是禁用的。
代碼創建綁定
CommandBinding binding = new CommandBinding(ApplicationCommands.New); binding.Executed+=binding_Executed; this.CommandBindings.Add(binding); xaml <Window.CommandBindings> <CommandBinding Command="ApplicationCommands.New" Executed="binding_Executed"></CommandBinding> </Window.CommandBindings> <StackPanel> <Button Command="ApplicationCommands.New">New</Button> </StackPanel>
使用多命令源
<Menu>
<MenuItem Header="File">
<MenuItem Command="New"></MenuItem>
</MenuItem>
</Menu>
微調命令文本
菜單可以自動提取命令項文本。button 要實現此功能。
<Button Command="New" Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}"></Button>
直接調用命令
ApplicationCommands.New.Execute(null, targetElement); this.CommandBindings[0].Command.Execute(null);
禁用命令
需求文本變時,保存按鈕才可用
public GridDemoWindow() { InitializeComponent(); CommandBinding binding = new CommandBinding(ApplicationCommands.Save); binding.Executed+=binding_Executed; binding.CanExecute+=binding_CanExecute; this.CommandBindings.Add(binding); } private void binding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = isDirty; } private void binding_Executed(object sender, ExecutedRoutedEventArgs e) { throw new NotImplementedException(); } private void txt_TextChanged(object sender,RoutedEventArgs e) { isDirty = true; }
自定義命令
public class DataCommands { private static RoutedUICommand requery; static DataCommands() { InputGestureCollection inputs = new InputGestureCollection(); inputs.Add(new KeyGesture(Key.R, ModifierKeys.Control, "Ctrl+R")); requery = new RoutedUICommand("Requery","Requery",typeof(DataCommands),inputs); } public static RoutedUICommand Requery { get { return requery; } } }
引用命名空間
xmlns:local="clr-namespace:Commands"
使用命令
<Window.CommandBindings>
<CommandBinding Command="local:DataCommands.Requery" Executed="binding_Executed"></CommandBinding>
</Window.CommandBindings>
<StackPanel>
<Button Command="local:DataCommands.Requery">Requery</Button>
</StackPanel>
