引言
在本貼中,我們將學習WPF Commands。 Commands 可以很好地與 MVVM 模式 (Model- View-ViewModel)結合在一起。我們也將看到,視圖(view)實際上是怎樣知道和怎樣調用它的使用WPF 命令( Commands )的視圖模型(ViewModel)。
背景
下面我們一步一步討論而不是立即查看完整的代碼,這也可以較好地幫助我們理解代碼的每一部分。
讓我們看一下MVVM的體系結構。

我們約定使用下列標准術語:
Views 表示后綴為view的文件名。(例如:StudentListView)ViewModels 表示后綴為ViewModel的文件。(例如:StudentListViewModel)Models 表示后綴為Model的文件。(例如:StudentModel).
使用代碼
原理介紹已經足夠了。下面深入代碼了解一個可以工作的MVVM例子,了解怎樣在MVVM使用命令。
使用 Visual Studio 建立一個新WPF項目。按照上面的約定,把文件名MainWindow更改為 MainWindowView。
接着,我們需要建立一個新的類,名字為 MainWindowViewModel ,它將擔當視圖MainWindowView的視圖模型(ViewModel)。
我們在這里所做的是,在MVVM內,我們告訴視圖,它的視圖模型是什么。這可以通過為視圖設置 Data Context來完成。在視圖模型文件里有ViewModel,然而他們現在還不具有某些特定的視圖之間的任何連接。
設置Datacontext的代碼看起來是下面的樣子。
打開 MainWindowView.xaml.cs並設置 data context 如下。
MainWindowView.xaml.cs
<Window x:Class="WpfExample.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:WpfExample">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
</Grid>
</Window>
這里的本地命名空間別名為WpfExample。這是必要的,這樣 framework知道MainWindowViewModel在哪里可以找到。
我們通過一個簡單的綁定來驗證這個。
讓我們添加一個button查看,使用視圖模型的一個實例設置button的content 。
視圖
添加一個查看按鈕並設置它的綁定內容如下。
MainWindowView.xaml.cs
<Window x:Class=" WpfMvvmExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WpfMvvmExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Button Width="100"
Height="100" Content="{Binding ButtonContent}"/>
</Grid>
</Window>
視圖模型
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
namespace
WpfExample
{
class
MainWindowViewModel
{
public
string
ButtonContent
{
get
{
return
"Click Me"
;
}
}
}
}
|
在上面代碼中,我們告訴視圖,從視圖模型所呈現的ButtonContent屬性獲取按鈕的內容。
<Button Width="100" Height="100" Content="{Binding ButtonContent}"/>
現在如果運行應用,我們可以看到按鈕的內容為 字符串(string) “Click Me”.

這表明我們的MVVM可以正常工作。
現在我們轉移到ICommand 接口(ICommand Interface)
現在,我們使用WPF命令為按鈕添加一個點擊功能。
在MVVM中,命令為通過視圖更新模型提供了一種機制。
首先,我們看一下ICommand接口。
bool CanExecute(object parameter); void Execute(object parameter); event EventHandler CanExecuteChanged;
我們建立一個應用樣板。當點擊按鈕時顯示一個“HI"消息框,我們添加另一個按鈕,切換hi按鈕是否可以點擊。
我們建立一個RelayCommand類,實現ICommand接口。這個類增強ICommand並分離代碼作為一個獨立的類。
This class acts as Enhancement for the ICommand and extracts the boiler plate code to a separate class.
public class RelayCommand : ICommand
{
private Action<object> execute; //定義成員
private Predicate<object> canExecute;//Predicate:述語//定義成員
private event EventHandler CanExecuteChangedInternal;//事件
public RelayCommand(Action<object> execute) //定義Action,CanExecute
: this(execute, DefaultCanExecute)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)//定義
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
if (canExecute == null)
{
throw new ArgumentNullException("canExecute");
}
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged //CanExecuteChanged事件處理方法
{
add
{
CommandManager.RequerySuggested += value;
this.CanExecuteChangedInternal += value;
}
remove
{
CommandManager.RequerySuggested -= value;
this.CanExecuteChangedInternal -= value;
}
}
public bool CanExecute(object parameter) //CanExecute方法
{
return this.canExecute != null && this.canExecute(parameter);
}
public void Execute(object parameter) //Execute方法
{
this.execute(parameter);
}
public void OnCanExecuteChanged() //OnCanExecute方法
{
EventHandler handler = this.CanExecuteChangedInternal;
if (handler != null)
{
//DispatcherHelper.BeginInvokeOnUIThread(() => handler.Invoke(this, EventArgs.Empty));
handler.Invoke(this, EventArgs.Empty);
}
}
public void Destroy() //銷毀方法
{
this.canExecute = _ => false;
this.execute = _ => { return; };
}
private static bool DefaultCanExecute(object parameter) //DefaultCanExecute方法
{
return true;
}
}
CommandManager.RequerySuggested 負責使能和禁用 "Click to Hii" 按鈕.
視圖
<Window x:Class="WpfExample.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:WpfExample">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Command="{Binding HiButtonCommand}"
CommandParameter="Hai" Content="{Binding HiButtonContent}"
Width="100"
Height="100" />
<Button Grid.Row="1" Content="Toggle Can Click"
Command="{Binding ToggleExecuteCommand}" Width="100" Height="100"/>
</Grid>
</Window>
視圖模型
class MainWindowViewModel
{
private ICommand hiButtonCommand;
private ICommand toggleExecuteCommand { get; set; }
private bool canExecute = true; //初始化為true
public string HiButtonContent //定義公開屬性
{
get
{
return "click to hi";
}
}
public bool CanExecute //定義公開屬性
{
get
{
return this.canExecute;
}
set
{
if (this.canExecute == value)
{
return;
}
this.canExecute = value;
}
}
public ICommand ToggleExecuteCommand //定義接口
{
get
{
return toggleExecuteCommand;
}
set
{
toggleExecuteCommand = value;
}
}
public ICommand HiButtonCommand //定義接口
{
get
{
return hiButtonCommand;
}
set
{
hiButtonCommand = value;
}
}
public MainWindowViewModel() //構造函數
{
HiButtonCommand = new RelayCommand(ShowMessage, param => this.canExecute);
toggleExecuteCommand = new RelayCommand(ChangeCanExecute);
}
public void ShowMessage(object obj) //消息 方法
{
MessageBox.Show(obj.ToString());
}
public void ChangeCanExecute(object obj) //方法
{
canExecute = !canExecute;
}
}
最后的運行結果好像是這樣:

