什么是命令?
命令是Windows Presentation Foundation(WPF)中的一種輸入機制,它提供比設備輸入更多的語義級別的輸入處理。
命令可以實現一處定義,處處使用的好處,不同的命令源只要綁定同一個命令就會執行該命令綁定的執行函數。
命令是一個獨立的操作,把這個操作分離出來的作用是增強程序的靈活性,比如復制命令在程序中很多地方都能用到,我們只要在不同的命令源綁定相同的命令,然后指定到不同的命令目標即可。
命令有幾個目的。第一個目的是將語義和調用命令的對象與執行命令的邏輯分開。這允許多個不同的源調用相同的命令邏輯,並且允許針對不同目標定制命令邏輯。例如,在許多應用程序中發現的編輯操作Copy,Cut和Paste可以通過使用不同的用戶操作(如果使用命令來實現)來調用。應用程序可能允許用戶通過單擊按鈕,選擇菜單中的項目或使用組合鍵(例如CTRL + X)來剪切選定的對象或文本。通過使用命令,可以將每種類型的用戶操作綁定到相同的邏輯。
命令的另一個目的是指示操作是否可用。繼續以剪切對象或文本為例,該操作僅在選擇某些內容后才有意義。如果用戶嘗試在沒有選擇任何內容的情況下剪切對象或文本,則不會發生任何事情。為了向用戶表明這一點,許多應用程序禁用了按鈕和菜單項,以便用戶知道是否可以執行某項操作。命令可以通過實現CanExecute方法指示是否可以執行某項操作。一個按鈕可以訂閱CanExecuteChanged事件,如果被禁用則CanExecute返回false
,或者如果啟用則CanExecute返回true
。
命令庫
WPF提供了一組預定義的命令。命令庫由以下類組成:ApplicationCommands,NavigationCommands,MediaCommands,EditingCommands和ComponentCommands。這些類提供諸如命令剪切,粘貼,復制,browseback和browseforward,播放,停止,暫停。
其中許多命令都包含一組默認輸入綁定。例如,如果指定您的應用程序處理復制命令,則會自動獲得鍵盤綁定“CTRL + C”。您還將獲得其他輸入設備的綁定,例如Tablet PC筆手勢和語音信息。
WPF命令中的四個主要概念
WPF中的路由命令模型可以分為四個主要概念:命令,命令源,命令目標和命令綁定:
-
命令是要執行的動作。
-
命令源是調用該命令的對象。
-
命令的目標是命令正在被執行的對象。
-
命令綁定是該命令邏輯映射到該命令的對象。
下面的示例演示如何設置MenuItem,以使其在單擊時將調用TextBox上的Paste命令(假定TextBox具有鍵盤焦點)。
<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste" />
</Menu>
<TextBox />
</StackPanel>
“粘貼”是命令,“菜單項”是命令源,“文本框”是命令目標,命令綁定由“文本框”控件提供。
指令
WPF中的命令是通過實現ICommand接口創建的。 ICommand公開了兩個方法Execute和CanExecute以及一個事件CanExecuteChanged。Execute執行與命令關聯的動作。CanExecute確定命令是否可以在當前命令目標上執行。如果集中命令操作的命令管理器檢測到命令源中的更改可能會使已引發但尚未由命令綁定執行的命令無效,則會引發CanExecuteChanged。ICommand的WPF實現是RoutedCommand類。
WPF中的主要輸入源是鼠標,鍵盤,路由命令。面向設備的更多輸入使用RoutedEvent來通知應用程序頁面中的對象已發生輸入事件。RoutedCommand是沒有什么不同。Execute和CanExecute的RoutedCommand不包含該命令的應用程序邏輯,而是它們提高該隧道和氣泡通過元素樹,直到它們遇到一個對象與路由事件的CommandBinding。CommandBinding包含這些事件的處理程序,它是執行該命令的處理程序。
RoutedCommand的Execute方法在命令目標上引發PreviewExecuted和Executed事件。在CanExecute上的方法的RoutedCommand引發CanExecute和PreviewCanExecute上命令目標的事件。這些事件在元素樹中經過隧道並冒泡,直到它們遇到一個具有針對該特定命令的CommandBinding的對象。
命令源
命令源是調用命令的對象。命令源的示例包括MenuItem,Button和KeyGesture(快捷鍵)。
WPF中的命令源通常實現ICommandSource接口。
ICommandSource公開了三個屬性:Command,CommandTarget和CommandParameter:
Command是調用命令源時要執行的命令。
CommandTarget是要在其上執行命令的對象。如果未設置CommandTarget,則具有鍵盤焦點的元素將成為命令目標。
CommandParameter是用戶定義的數據類型,用於將信息傳遞給實現命令的處理程序。
實現ICommandSource的WPF類是ButtonBase,MenuItem,Hyperlink和InputBinding。 單擊它們時,ButtonBase,MenuItem和Hyperlink會調用命令,而執行與之關聯的InputGesture時,InputBinding會調用命令。
通常,命令源將偵聽CanExecuteChanged事件。此事件通知命令源該命令在當前命令目標上執行的能力可能已更改。命令源可以使用CanExecute方法查詢RoutedCommand的當前狀態。如果命令無法執行,則命令源可以禁用自身。例如,當無法執行命令時,MenuItem會自動變灰。
一個InputGesture可以用作命令源。WPF中的兩種輸入手勢是KeyGesture和MouseGesture。您可以將KeyGesture視為鍵盤快捷鍵,例如CTRL + C。
為了使InputGesture充當命令源,它必須與命令關聯。有幾種方法可以完成此操作。一種方法是使用InputBinding。
下面的示例演示如何在KeyGesture和RoutedCommand之間創建KeyBinding。即創建把快捷鍵當成命令源。
<Window.InputBindings>
<KeyBinding Key="B" Modifiers="Control" Command="ApplicationCommands.Open" /> //按快捷鍵B時執行打開命令
</Window.InputBindings>
將InputGesture關聯到RoutedCommand的另一種方法是將InputGesture添加到RoutedCommand的InputGestureCollection上。
KeyGesture OpenCmdKeyGesture = new KeyGesture( Key.B, ModifierKeys.Control);
ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);
命令綁定
CommandBinding關聯與執行命令的事件處理程序的命令。
CommandBinding類包含一個命令屬性,PreviewExecuted,Execute,PreviewCanExecute和CanExecute事件。
下面的示例演示如何在應用程序的根窗口上創建CommandBinding。
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Open"
Executed="OpenCmdExecuted"
CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
//C#代碼實現方式
CommandBinding OpenCmdBinding = new CommandBinding(
ApplicationCommands.Open,
OpenCmdExecuted,
OpenCmdCanExecute);
this.CommandBindings.Add(OpenCmdBinding);
接下來,創建ExecutedRoutedEventHandler和CanExecuteRoutedEventHandler。該ExecutedRoutedEventHandler打開一個消息框,其中顯示的字符串說命令已被執行。該CanExecuteRoutedEventHandler設置CanExecute屬性true。
void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
String command, targetobj;
command = ((RoutedCommand)e.Command).Name;
targetobj = ((FrameworkElement)target).Name;
MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj);
}
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
命令目標
命令目標是在其上執行命令的元素。關於RoutedCommand,命令目標是Executed和CanExecute路由開始的元素。如果在ICommandSource上設置了CommandTarget並且相應的命令不是RoutedCommand,則將忽略命令目標。
命令源可以顯式設置命令目標。如果未定義命令目標,則將具有鍵盤焦點的元素用作命令目標。將具有鍵盤焦點的元素用作命令目標的好處之一是,它使應用程序開發人員可以使用同一命令源在多個目標上調用命令,而不必跟蹤命令目標。例如,如果MenuItem在具有TextBox控件和PasswordBox控件的應用程序中調用“粘貼”命令,則目標可以是TextBox或PasswordBox,具體取決於哪個控件具有鍵盤焦點。
下面的示例演示如何在標記和后面的代碼中顯式設置命令目標。
<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste"
CommandTarget="{Binding ElementName=mainTextBox}" />
</Menu>
<TextBox Name="mainTextBox"/>
</StackPanel>
例子:
xaml代碼:
<Window x:Class="WpfTest.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfTest"
mc:Ignorable="d"
Title="TestWindow" Height="300" Width="300">
<StackPanel>
<TextBox x:Name="txtMsg" Text="123" />
<!--第四步:把命令綁定到命令源-->
<Button Content="clickme"
Command="{x:Static local:TestWindow.AddCommand}"
CommandParameter="{Binding Text ,ElementName=txtMsg}" />
</StackPanel>
</Window>
cs代碼:
using System.Windows;
using System.Windows.Input;
namespace WpfTest
{
/// <summary>
/// TestWindow.xaml 的交互邏輯
/// </summary>
public partial class TestWindow : Window
{
//第一步:定義命令
public static ICommand AddCommand = new RoutedCommand();
//第二步:定義執行函數
public void Add(object sender, ExecutedRoutedEvente e)
{
MessageBox.Show(e.Parameter.ToString());
//標記為已處理,不再向上傳遞
e.Handled = true;
}
public void CanAdd(object sender, CanExecuteRoutedEvente e)
{
if (string.IsNullOrEmpty(e.Parameter.ToString()))
e.CanExecute = false;
else
e.CanExecute = true;
e.Handled = true;
}
public TestWindow()
{
InitializeComponent();
//第三步:關聯命令和執行函數,並添加到窗體的命令綁定集
this.CommandBindings.Add(new CommandBinding(AddCommand, Add, CanAdd));
}
}
}