1) 命令(Command):WPF的命令实际上就是实现了ICommand接口的类,平时使用最多的是RoutedCommand类。
2) 命令源(Command Source):即命令的发送者,是实现了ICommandSource接口的类。很多界面元素都实现了这个接口,包括Button、MenuItem、ListBoxItem等。
3) 命令目标(Command Target):即命令将发送给谁,或者说命令将作用在谁身上。命令目标必须是实现了IInputElement接口的类。
4) 命令关联(Command Binding):负责把一些外围逻辑与命令关联起来,比如执行之前对命令是否可以执行进行判断、命令执行之后还有哪些后续工作等。
基础命令, 这是我的叫法, 有其他叫法的人忽略就可以了。
基础命令指的是WPF已经给我们定义好了的命令, 类似已经定义好了的事件(MouseDown)。
主要分为以下几类:
public void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
//设置成true, 其实最终就是RoutedCommand的CanExcute函数返回true.
//所以结果就是命令源一直可以使用
if (null != textBox && textBox.Text.Length > 0)//有内容才允许打印
e.CanExecute = true;
else //如果 e.CanExecute = false的话命令源就不能使用
e.CanExecute = false;
}
public void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
//具体的打印逻辑
MessageBox.Show("我要打印了");
}
MenuItem Command="{x:Static ApplicationCommands.Print}"
CommandTarget="{Binding ElementName=textBox}"
class SelfDefineCommand1
{
///
/// 这就是自定义的命令了, 其实就是创建了一个RoutedCommand的静态对象
///
public static RoutedCommand MyFirstCommandForPrint = new RoutedCommand("MyCommand", typeof(SelfDefineCommand1),
new InputGestureCollection() { new KeyGesture(Key.P, ModifierKeys.Control) });
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace CommandDemo
{
class SelfDefineCommand2 : ICommand
{
//CommandManager.RequerySuggested WPF底层代码自动调用,比如
//MenuItem显示时, TextBox聚焦时, 最终通过CanExecute返回的值设置
//控件是否可用的状态
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
//见CanExcuteChanged注释
public bool CanExecute(object parameter)
{
TextBox textBox = parameter as TextBox;
if(null != textBox && textBox.Text.Length > 0)//存在内容, 允许打印
{
return true;
}
else//不存在内容,不允许打印
{
return false;
}
}
//执行具体的业务逻辑
public void Execute(object parameter)
{
TextBox textBox = parameter as TextBox;
if(null != textBox)
{
MessageBox.Show(textBox.Text);
}
}
///
/// 自定义一个静态的SelfDefineCommand2对象
///
public static SelfDefineCommand2 MyCommandForPrint2 = new SelfDefineCommand2();
}
}
如代码所示, SelfDefineCommand2类实现了ICommand接口的CanExecute和Execute函数, 以及CanExecuteChanged事件,具体解释清参见代码注释。
CommandTarget="{Binding ElementName=textBox3}"
CommandParameter="{Binding ElementName=textBox3}
行发送事件等操作, 实际上CommandBinding拦截到的是一个事件; 所以现在因为不需要发送事件了, 也就不需要事件监听器了。 具体的请参见第3节的原理分析。
protected virtual void OnClick()
{
RoutedEventArgs newEvent = new RoutedEventArgs(ButtonBase.ClickEvent, this);
RaiseEvent(newEvent);
MS.Internal.Commands.CommandHelpers.ExecuteCommandSource(this);
}
这个是Button父类的OnClick函数, Button.OnClick()总是会调用父类的Onclick函数,并在父类OnClick函数中调用下面这个函数
CommandHelpers.ExecuteCommandSource(this)
[SecurityCritical, SecurityTreatAsSafe]
internal static void ExecuteCommandSource(ICommandSource commandSource)
{
CriticalExecuteCommandSource(commandSource, false);
}
[SecurityCritical]
internal static void CriticalExecuteCommandSource(ICommandSource commandSource, bool userInitiated)
{
ICommand command = commandSource.Command;
if (command != null)
{
object parameter = commandSource.CommandParameter;
IInputElement target = commandSource.CommandTarget;
RoutedCommand routed = command as RoutedCommand;
if (routed != null)
{
if (target == null)
{
target = commandSource as IInputElement;
}
if (routed.CanExecute(parameter, target))
{
routed.ExecuteCore(parameter, target, userInitiated);
}
}
else if (command.CanExecute(parameter))
{
command.Execute(parameter);
}
}
}
你一定看到了CriticalExecuteCommandSource函数中的这个代码片段,
if (routed != null)
{
if (target == null)
{
target = commandSource as IInputElement;
}
if (routed.CanExecute(parameter, target))
{
routed.ExecuteCore(parameter, target, userInitiated);
}
}
routed局部变量就是RoutedCommand的对象引用,意思就是如果command对象的类型是RoutedCommand的话, 就执行RoutedCommand的ExecuteCore,
[SecurityCritical]
internal bool ExecuteCore(object parameter, IInputElement target, bool userInitiated)
{
if (target == null)
{
target = FilterInputElement(Keyboard.FocusedElement);
}
return ExecuteImpl(parameter, target, userInitiated);
}
[SecurityCritical]
private bool ExecuteImpl(object parameter, IInputElement target, bool userInitiated)
{
// If blocked by rights-management fall through and return false
if ((target != null) && !IsBlockedByRM)
{
UIElement targetUIElement = target as UIElement;
ContentElement targetAsContentElement = null;
UIElement3D targetAsUIElement3D = null;
// Raise the Preview Event and check for Handled value, and
// Raise the regular ExecuteEvent.
ExecutedRoutedEventArgs args = new ExecutedRoutedEventArgs(this, parameter);
args.RoutedEvent = CommandManager.PreviewExecutedEvent;
if (targetUIElement != null)
{
targetUIElement.RaiseEvent(args, userInitiated);
}
else
{
targetAsContentElement = target as ContentElement;
if (targetAsContentElement != null)
{
targetAsContentElement.RaiseEvent(args, userInitiated);
}
else
{
targetAsUIElement3D = target as UIElement3D;
if (targetAsUIElement3D != null)
{
targetAsUIElement3D.RaiseEvent(args, userInitiated);
}
}
}
if (!args.Handled)
{
args.RoutedEvent = CommandManager.ExecutedEvent;
if (targetUIElement != null)
{
targetUIElement.RaiseEvent(args, userInitiated);
}
else if (targetAsContentElement != null)
{
targetAsContentElement.RaiseEvent(args, userInitiated);
}
else if (targetAsUIElement3D != null)
{
targetAsUIElement3D.RaiseEvent(args, userInitiated);
}
}
return args.Handled;
}
return false;
}
可以看到最终是调用了ExecuteImpl函数, 这个函数中主要看如下代码片段:
ExecutedRoutedEventArgs args = new ExecutedRoutedEventArgs(this, parameter);
args.RoutedEvent = CommandManager.PreviewExecutedEvent;
if (targetUIElement != null)
{
targetUIElement.RaiseEvent(args, userInitiated);
}
简而言之, 就是命令目标控件发送了一个 PreviewExecutedEvent事件,最终来触发CommandBinding监听器的Executed事件处理函数。这也是为什么RoutedCommand需要
[SecurityCritical]
internal static void CriticalExecuteCommandSource(ICommandSource commandSource, bool userInitiated)
{
ICommand command = commandSource.Command;//命令源中去处Command对象
if (command != null)
{
object parameter = commandSource.CommandParameter;
IInputElement target = commandSource.CommandTarget;
RoutedCommand routed = command as RoutedCommand;//强转Command对象为RoutedCommand对象
if (routed != null)
{
if (target == null)
{
target = commandSource as IInputElement;
}
if (routed.CanExecute(parameter, target))
{
routed.ExecuteCore(parameter, target, userInitiated);
}
}
else if (command.CanExecute(parameter))//如果Command对象不为RoutedCommand类型的话,就直接执行Command中的Execute.
{
command.Execute(parameter);
}
}
}
请关注代码中注释的地方, 关键在于 "
如果Command对象不为RoutedCommand类型的话,就直接执行Command中的Execute" 这句话, 这样也就不会发送事件了。