这次讲解的命令绑定,主要解决的问题是,为实现MVVM模式进行铺垫,实现前后台逻辑的解耦。
我们知道如果Button直接实现Click事件,那么实现的逻辑必然在Window后台代码中,为了实现MVVM,我要将业务逻辑放在ViewMode里面,这时需要Command Binding。
前台代码:
- <Button Grid.Row="2"
- Command="{Binding BtnSaveCommand}"
- CommandParameter="{Binding RelativeSource={RelativeSource Self}}">保存Button>
然后创建CommandBase类,该类实现ICommand接口。
ICommand需要实现一个事件:CanExecuteChanged
两个方法: Execute, CanExecute
Execute 是命令促发后,系统会调用的回调函数
CanExecute 是当CanExecuteChanged事件触发后,体统会调用它,并更具它的返回值判断控件是否可用。
- public class CommandBase : ICommand
- {
- public event EventHandler CanExecuteChanged;
- public Action<object> DoExecute { get; set; }
-
- // 这里给个默认的值,不实现就返回true
- public Func<object, bool> DoCanExecute { get; set; } = new Func<object, bool>(obj => true);
-
- public bool CanExecute(object parameter)
- {
- // 让实例去实现这个委托
- return DoCanExecute?.Invoke(parameter) == true;// 绑定的对象 可用
- }
-
- public void Execute(object parameter)
- {
- // 让实例去实现这个委托
- DoExecute?.Invoke(parameter);
- }
-
-
- //目的 就是触发一次CanExecuteChanged事件
- public void DoCanExecuteChanged()
- {
- // 触发事件的目的就是重新调用CanExecute方法
- CanExecuteChanged?.Invoke(this, EventArgs.Empty);
- }
- }
- public CommandBase BtnSaveCommand { get; set; }
-
- public ConfigInfoViewMode()
- {
-
- BtnSaveCommand = new CommandBase()
- {
- DoExecute = new Action<object>(SaveTo),
- DoCanExecute = new Func<object, bool>(CanSave)
- };
- }
-
- //最终按钮会触发到改函数
- void Save(object obj)
- {
-
- }
-
- //决定按键是否可用(这也是命令额外的一个作用)
- // 通过调用DoCanExecuteChanged,可以触发该元素
- public bool CanSave(object obj)
- {
- return true;
- }
- //目的 就是触发一次CanExecuteChanged事件
- public void DoCanExecuteChanged()
- {
- // 触发事件的目的就是重新调用CanExecute方法
- CanExecuteChanged?.Invoke(this, EventArgs.Empty);
- }
触发事件的目的就是重新调用CanExecute方法,从而从新判断控件是否可用。
但是这个DoCanExecuteChanged(),是需要我们人为调用的。
另外一种写法可以让系统去调用,我们需要一个新的知识点CommandManager
以下两种写法都可以,只需要在定义CanExecuteChanged时稍微改动一下:
- // 通知:当前按钮检查可用条件 触发 一次CanExecute
- // 写法一
- public event EventHandler? CanExecuteChanged
- {
- add { CommandManager.RequerySuggested += value; }
- remove { CommandManager.RequerySuggested -= value; }
- }
-
- //写法二
- public event EventHandler? CanExecuteChanged
- {
- add => CommandManager.RequerySuggested += value;
- remove => CommandManager.RequerySuggested -= value;
- }
这里的意思是,通过静态变量CommandManager将CanExecuteChanged挂载到,RequerySuggested 这个事件上,这里的value是指CanExecuteChanged本身,下图可以证明。(这个和属性的value有着异曲同工之妙)

从此之后,这个检测工作交给系统。
我发现这个检测的时机其实和绑定有关。
- <TextBox Text="{Binding mainModel.Value1,UpdateSourceTrigger=PropertyChanged}"/>
- <TextBox Text="{Binding mainModel.Value3}"/>
我们在检擦函数中这么写的:
- // Hello命令的的可用判断逻辑
- public bool CanHello(object obj)
- {
- // 命令可用条件
- // 当调用 CanExecuteChanged 事件后,重新进行条件检查
- return this.mainModel.Value1 != 0;
- }
-
-
- public bool CanWorld(object obj)
- {
- return this.mainModel.Value3 != 0;
- }
也就是说,TextBox中的值会决定Button是否可用!
那么第一个TextBox的值,只要一发生改变,那么就会触发检查是否可用,而第二个TextBox只有
失去焦点的时候,才会触发检查。
这里有个小疑问,我们的Command是写在Button上的,所以判断是Button是否可用,同时Command实现了CanExecuteChanged事件,并挂载到了全局,以便系统调用,那系统是怎么知道
变化的Value和Button的关系的呢?也不知道微软是什么时候将这个指和Button关联上的。
当然,此时如果你觉得你想在某个时刻进行强制触发检测,也是可以的,还是使用CommandManager这个静态变量:
CommandManager.InvalidateRequerySuggested();
写道这里,其实还存在一个问题,Button的Command是由但是触发的,如果我想通过双击或者快捷键触发怎么办呢?
InputBindings,满足你的需求:
- <Button Content="Button">
- <Button.InputBindings>
- <MouseBinding MouseAction="LeftDoubleClick"
- Command="{Binding Btn1Command}"
- CommandParameter="123"/>
- <KeyBinding Key="Q" Modifiers="Alt"
- Command="{Binding Btn1Command}"
- CommandParameter="123"/>
- Button.InputBindings>
- Button>
MouseBinding 中的MouseAction还有如下操作:
KeyBinding 可以帮我们实现快捷键和快捷键组合,简直完美~~。