Pro WPF and Silverlight MVVM:第5章 Event and Command 读书笔记
时间:2011-06-11 来源:davin
Event在是程序结构中影响状态变化的部分。基本上说,Event在.net里无处不在,wpf和silverlight也不例外,事件被用作通过鼠标和键盘来通知用户输入.但不局限于此, 控件在wpf和silverlight中暴露那些允许被订阅的事件,就像window forms 和asp.net.不同之处在于这些事件的实现,以及他们具备怎样的行为。
WPF和silverlight不用CLR事件,而是使用routed events.采用不同的方法的主要原因是由于在一个元素树上可能有多个事件订阅者.因此CLR事件直接调用事件订阅者处理,routed event可以被事件订阅者在元素树上的任何祖先处理.(通俗的说法:事件冒泡)
Command
command pattern(GOF)封装command的功能,分离调用者和接收者。
command帮助我们克服了事件的限制因为我们能在view里面触发一个command并且决定接受它在ViewModel,它能知道在应该在响应中发生什么样的处理。
不仅那些,command的多个调用者保证了我们定义交互逻辑一次就可以在许多不同的第凡使用它并且不重复。
Command Pattern
Command pattern在wpf和silverlight中是通过接口 和Invoker, Receiver, and Command interface类似的command接口。理解实体间是如何协作将会更加突出为什默认的command 实现Routed Command和RoutedUICommand,他们不是完全的适合我们的构想。因此提供一个可替代的实现,那样更适合MVVM架构.
ICommandInterface
public interface ICommandSource
{
ICommand Command { get; }
object CommandParameter { get; }
IInputElement CommandTarget { get; }
}
这种设计可以清晰看出每个执行ICommandSource接口的类只能暴露一个Command属性,解释下为什么大部分时间信任事件,如果事件想要暴露多个Command,整个架构也会阻止的。
ICommand Interface
public interface ICommand
{
event EventHandler CanExecuteChanged;
bool CanExecute(object parameter);
void Execute(object parameter);
}
所有的Command觉得继承自ICommand interface,Excute方法是Command模式的核心,他封装的Command都会执行。我们可以提供Execute需要的任何参数,以至于
可以通过上下文传递来自定义执行。
CanExcute方法通知调用者这个Command是否能执行,实际上它是自动允许和不允许和调用者使用。服用之前的例子退出应用程序,一个长时间运行的进程不能被中断,因此应用程不能退出。除非你提供一个提示给用户,应用程序不能现在退出。
Routed Command Implementations
wpf 和silvelight有2个ICommand的实现:RoutedCommand 和RoutedUICommand
RoutedCommand RoutedUICommand的区别就是RoutedCommand 有一个Text属性
触发一个Command的流程。
RoutedCommand
RoutedCommand调用一个Commangd Manage Execued 方法.
问题是:CommandManager 和CommandBinding 是一组非直接,和viewmodel的需求不符
RelayCommand
优点:
1.保持view上的控件和相应用户输入的处理程序分离
2.RelayCommand作为ViewModel类的实例
3.响应RelayCommand的处理在同一个类里。如果不是直接的,处理程序也会通过实力的属性字段来访问
4.CanExecute 和CanExecuteChanged被实现并且功能扩展了,保证ICommandSource是enabled and disabled
5.CanExecute的参数时可选,没有指定处理程序,Command默认是true
6.CanExecute会被ViewModel处理
7.保持最小的额外的依赖
例子:
public class LogInViewModel
{
public LogInViewModel()
{
_logInModel = new LogInModel();
_logInCommand = new RelayCommand(param => this.AttemptLogIn(), param =>
this.CanAttemptLogIn());
}
public string UserName
{
get;
set;
}
public string Password
{
get;
set;
}
public ICommand LogInCommand
{
get
{
return _logInCommand;
}
}
private void AttemptLogIn()
{
_logInModel.LogIn(UserName, Password);
}
private bool CanAttemptLogIn()
{
return !String.IsNullOrWhiteSpace(UserName) && !String.IsNullOrWhiteSpace(Password);
}
private RelayCommand _logInCommand;
private LogInModel _logInModel;
}