在使用 MVVM 結(jié)構(gòu)的 WPF 程序中,ViewModel 對 View 的響應(yīng)是通過 Command (命令)來完成的。比如用戶點(diǎn)了某個(gè)按鍵,使得與該按鍵相綁定的命令被觸發(fā),而該命令又是在 ViewModel 中被定義的,于是就完成了一次 View 對 ViewModel 的調(diào)用。
WPF 和 C# 語言的高級用法,比如委托、反射、事件等等,是緊密聯(lián)系的,所以學(xué)習(xí) WPF 時(shí)需要有一定的 C# 基礎(chǔ)。
雖然官方提供了一些常用命令,但本文還是著重于自定義命令的用法。
?? 方法一 從頭開始創(chuàng)建自定義命令
第一步:創(chuàng)建命令
命令有兩大要素:做什么、能做嗎。自定義命令首先要繼承自 ICommand:
class CustomCommand : ICommand
{
//當(dāng)能不能做發(fā)生變化時(shí)會觸發(fā)的事件(必須要實(shí)現(xiàn))
public event EventHandler CanExecuteChanged;
public void Execute(object param) //做什么(必須要實(shí)現(xiàn))
{
ExecuteAction?.Invoke(param);
}
public bool CanExecute(object param) //能做嗎(必須要實(shí)現(xiàn))
{
if (CanExecuteAction != null)
return CanExecuteAction(param);
return false;
}
public Action<object> ExecuteAction { get; set; }
public Func<object, bool> CanExecuteAction { get; set; }
}
如上所示,ICommand 的后代有三個(gè)東西必須要實(shí)現(xiàn),而最后兩個(gè) ExecuteAction 和 CanExecuteAction 是比較推薦的寫法。
在本例中,Execute() 方法是 ICommand 認(rèn)定的用來執(zhí)行命令的方法,ExecuteAction 是對該方法的一項(xiàng)委托,于是外部使用者(ViewModel)只需設(shè)置這份委托,而不需染指 Execute() 方法。
第二步:完成ViewModel
接下來把這個(gè)命令類放在 ViewModel 中使用:
//先實(shí)例化這個(gè)命令(這是屬于ViewModel的命令,等下要被送到View中去)
public CustomCommand MyCommand { get; set; }
public void DoSomething(object param){
//這個(gè)命令真正要做的事情
}
public bool CanDoSomething(object param){
return true; //判斷能否做這個(gè)事情,大部分時(shí)候返回true就行了
}
public MyViewModel(){
//在ViewModel的構(gòu)造函數(shù)中,完成對命令的設(shè)置
MyCommand = new CustomCommand();
MyCommand.ExecuteAction = new Action<object>(this.DoSomething);
MyCommand.CanExecuteAction = new Func<object, bool>(this.CanDoSomething);
}
ViewModel 中定義了兩個(gè)函數(shù),分別對應(yīng)命令的“做什么”、“能做嗎”,并通過委托的方式告訴命令。
第三步:完成View
剩下的就是 View 中的內(nèi)容了:
先要加入命名空間,才能獲得我們的 ViewModel。這里把命名空間設(shè)為vm
<Window xmlns:vm="clr-namespace:MyApp.ViewModel" ... />
<Grid>
<Grid.DataContext>
<vm:MyViewModel> <!--把第二步中的ViewModel當(dāng)作DataContext-->
</Grid.DataContext>
<Button Content="Click here" Command="{Binding MyCommand}" />
</Grid>
</Window>
在 View 的 xaml 文件中,首先把 ViewModel 當(dāng)作它的 DataContext (畢竟 ViewModel 其實(shí)就是 View 的 Model)。然后直接在按鈕上用 Binding 的方式綁定命令。于是當(dāng)用戶點(diǎn)擊按鈕時(shí),這個(gè)命令就會被觸發(fā)。
命令被觸發(fā)時(shí),它會先調(diào)用自己的 CanExecute() 方法,這個(gè)方法在第一步中調(diào)用 CanExecuteAction 這個(gè)委托,這份委托又在第二步中綁定了 CanDoSomething() 方法……經(jīng)過一番長途跋涉之后終于調(diào)用了 DoSomething() 方法 ??。
感受:如果按照老辦法,按鈕響應(yīng)只需設(shè)一個(gè) OnClick 的方法就成了,簡單清爽,完全不必大費(fèi)周章搞這么多事??僧?dāng)程序到了一定規(guī)模,舊的思路就使維護(hù)變得難以維系。做界面還是應(yīng)該養(yǎng)成良好的習(xí)慣
?? 方法二 使用自帶的 RoutedUICommand
先創(chuàng)建一個(gè)命令:
public static class CustomCommands
{
public static readonly RoutedUICommand ExitCommand = new RoutedUICommand(
"quit app",
"ExitCommand",
typeof(CustomCommands),
new InputGestureCollection() {
new KeyGesture(Key.W, ModifierKeys.Control) //可以為它綁定快捷鍵
});
}
然后為“做什么”和“能做嗎”定義兩個(gè)方法:
public void ExitCommand_Execute(object sender, ExecutedRoutedEventArgs e)
{
//在這里寫執(zhí)行命令的具體內(nèi)容(要做什么事)
e.Handled = true;
}
public void ExitCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true; //在這里判斷該命令能否執(zhí)行
e.Handled = true;
}
最后在 xaml 里面進(jìn)行綁定:
<Window.CommandBindings>
<CommandBinding Command="local:CustomCommands.ExitCommand"
CanExecute="ExitCommand_CanExecute"
Executed="ExitCommand_Execute"/>
</Window.CommandBindings>
<Button Content="Exit" Command="local:CustomCommands.ExitCommand"/>