前言
這次講解的指令綁定,主要解決的問題是,為實作MVVM模式進行鋪墊,實作前背景邏輯的解耦。
我們知道如果Button直接實作Click事件,那麼實作的邏輯必然在Window背景代碼中,為了實作MVVM,我要将業務邏輯放在ViewMode裡面,這時需要Command Binding。
Command Binding
使用Command 替換 Click
前台代碼:
<Button Grid.Row="2"
Command="{Binding BtnSaveCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}">儲存</Button>
建立CommandBase類
然後建立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);
}
}
ViewMode中執行個體化指令
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
如果不想手動調用DoCanExecuteChanged(),還有一個辦法就是CommandManager。
另外一種寫法可以讓系統去調用,我們需要一個新的知識點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
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還有如下操作:
- 單擊滑鼠左鍵 :LeftClick
- 輕按兩下滑鼠左鍵:LeftDoubleClick
- 單擊滑鼠中鍵 :MiddleClick
- 輕按兩下滑鼠中鍵:MiddleDoubleClick
- 單擊滑鼠右鍵:RightClick
- 輕按兩下滑鼠右鍵:RightDoubleClick
- 不執行任何操作:None
- 旋轉滑鼠滾輪:WheelClick
KeyBinding
KeyBinding 可以幫我們實作快捷鍵和快捷鍵組合,簡直完美~~。