对于不太了解MVVM模式的读者在读这篇文章之前,应当先看看我之前的一片文章。
http://www.cnblogs.com/zhangzhi19861216/archive/2013/03/19/WPF-MVM.html
例3:命令
绑定到GUI事件是有问题的, WPF为我们提供了一个更好的方式,这就是ICommand。大多控件都有一个命令特性。对于简单示例,我们在这里只实现一个简单的类RelayCommand,它继承ICommand。
ICommand接口要求用户定义两个方法:bool CanExecute(object parameter)和void Execute(object parameter)。 CanExecute方法,定义用于确定此命令是否可以在其当前状态下执行的方法。在我们的例子中,我们不关心它,所以我们返回true,这意味着,我们一直可以执行命令。
因为我们想重用ICommand的代码,所以我们使用继承Icommand接口的类RelayCommand,它包含所有我们不想多写的复用代码。
为了表明ICommand是多么容易复用,我们绑定更新艺术家命令到一个按钮和一个菜单项。注意,我们不再绑定到特定按钮的Click事件,或菜单的Click事件。
让我们来看看代码:
在例3中我们添加了一个RelayCommand.cs文件,代码如下:
public class RelayCommand : ICommand
{
#region Members
readonly Func<Boolean> _canExecute;
readonly Action _execute;
#endregion
#region Constructors
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func<Boolean> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
[DebuggerStepThrough]
public Boolean CanExecute(Object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(Object parameter)
{
_execute();
}
#endregion
译者附加:
WPF的命令是实现了ICommand接口的类,ICommand接口非常简单,包含两个方法和一个事件。
如上代码,RelayCommand类继承了ICommand接口,因此实现了ICommand接口的两个方法和一个事件。
在RelayCommand类中我们定义了两个委托
readonly Func<Boolean> _canExecute;
readonly Action _execute;
_canExecute封装一个没有参数的方法,此方法有一个返回值,用在Boolean CanExecute(Object parameter)当中判断这个命令是否可以执行。
_execute封装一个既没有参数也没有返回值的方法,用在Execute(Object parameter)方法当中定义调用此命令时调用的方法。
CanExecuteChanged:当出现影响是否应执行该命令的更改时发生。通常,当发生该事件时,命令源对该命令调用 CanExecut,如果该命令无法执行,则命令源禁用其自身。
再来看看例3中SongViewModel类的代码,在例2的代码基础上,多了如下代码:
int _count = 0;
void UpdateArtistNameExecute()
{
++_count;
ArtistName = string.Format("Elvis ({0})", _count);
}
bool CanUpdateArtistNameExecute()
{
return true;
}
public ICommand UpdateArtistName { get { return new RelayCommand(UpdateArtistNameExecute, CanUpdateArtistNameExecute); } }
在SongViewModel类中定义了一个ICommand接口类型的属性UpdateArtistName,get返回的是RelayCommand类型的一个实例,其中new RelayCommand实例时构造函数传进了两个方法。让我们转到定义看一下。
又回到RelayCommand类中,看到如下代码:
public RelayCommand(Action execute, Func<Boolean> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
在这里我们看到exeute指向传进来的UpdateArtistNameExecute方法,_canExecute指向CanUpdateArtistNameExecute方法。
再来看看view及MainWindow.xaml,
<Window.DataContext>
<local:SongViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Menu Grid.Row="0" Grid.ColumnSpan="3">
<MenuItem Header="Test">
<MenuItem Header="Update Artist" Command="{Binding UpdateArtistName}" />
</MenuItem>
</Menu>
<Label Grid.Column="0" Grid.Row="1" Content="Example 3 - using ICommand!" />
<Label Grid.Column="0" Grid.Row="2" Content="Artist: " />
<Label Grid.Column="1" Grid.Row="2" Content="{Binding ArtistName}" />
<Button Grid.Column="1" Grid.Row="3" Name="ButtonUpdateArtist" Content="Update Artist Name" Command="{Binding UpdateArtistName}" />
</Grid>
我们看到按钮和菜单项的命令都绑定到SongViewModel类实例的UpdateArtistName命令属性上。源代码清晰了,我们接下来看看WPF 的命令系统(引用《深入浅出WPF》):
命令系统构成要素:
命令Command
命令源Command Source
命令目标Command Target
命令关联 CommandBinding
基本元素之间的关系
创建命令类:即获得一个实现ICommand接口的类。
声明命令实例:使用命令时需要创建命令类的实例。
指定命令源:由谁来发送这个命令。
指定命令目标:命令目标不是命令的属性而是命令源的属性(疑问)。
设置命令关联。
好了,现在我们又来分析我们的源代码。
1、 创建命令类,我们的RelayCommand类。
2、 声明命令实例,SongViewModel类中声明了一个命令属性UpdateArtistName.
3、 指定命令源:按钮和菜单项
4、 指定命令目标,绑定到ArtistName属性的Lable控件。
5、 命令关联,RelayCommand中CanExecute和Execute执行的方法。
运行程序,点击按钮,激发UpdateArtistName命令,如果CanExecute方法返回true(我们这里一直返回true),则执行Execute方法。即调用UpdateArtistNameExecute()方法更新ArtistName属性,因为ArtistName属性实现了INotifyPropertyChanged
接口的PropertyChangedEventHandler事件,所以Lable的内容跟着改变。
代码地址:files.cnblogs.com/zhangzhi19861216/MVVMQuickStartTutorial.rar