引言
在本貼中,我們將學習WPF Commands。 Commands 可以很好地與 MVVM 模式 (Model- View-ViewModel)結合在一起。我們也將看到,視圖(view)實際上是怎樣知道和怎樣調用它的使用WPF 命令( Commands )的視圖模型(ViewModel)。
背景
下面我們一步一步討論而不是立即查看完整的代碼,這也可以較好地幫助我們理解代碼的每一部分。
讓我們看一下MVVM的體系結構。
我們約定使用下列標准術語:
View
s 表示后綴為view的文件名。
(例如:StudentListView
)ViewModel
s 表示后綴為ViewModel的文件。(例如:StudentListViewModel
)Model
s 表示后綴為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; } }