wpf命令详解
什么是命令?
命令是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));
}
}
}