Chapter1 MVVM概念及常用基类
一、MVVM的概念
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。微软的WPF带来了新的技术体验,如Silverlight、音频、视频、3D、动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了 诸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新特性。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。
二、ViewModel中两个常用基类
NotifycationBase
NotifycationObject 的主要作用,是提供 OnPropertyChange(string propertyName) 方法。
using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
namespace LoginDemo
{
/// <summary>
/// 可提示属性更改事件的对象
/// </summary>
public abstract class NotifycationBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void RaisePropertyChanged(params string[] propertyNames)
{
if (propertyNames == null) throw new ArgumentNullException("propertyNames");
foreach (var name in propertyNames)
{
this.RaisePropertyChanged(name);
}
}
protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
var propertyName = ExtractPropertyName(propertyExpression);
this.RaisePropertyChanged(propertyName);
}
public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
if (propertyExpression == null)
{
throw new ArgumentNullException("propertyExpression");
}
var memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
{
throw new ArgumentException("PropertySupport_NotMemberAccessExpression_Exception", "propertyExpression");
}
var property = memberExpression.Member as PropertyInfo;
if (property == null)
{
throw new ArgumentException("PropertySupport_ExpressionNotProperty_Exception", "propertyExpression");
}
var getMethod = property.GetGetMethod(true);
if (getMethod.IsStatic)
{
throw new ArgumentException("PropertySupport_StaticExpression_Exception", "propertyExpression");
}
return memberExpression.Member.Name;
}
}
}
RelayCommandBase
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace LoginDemo1.ViewModels
{
public class RelayCommandBase : ICommand
{
#region Fields
/// <summary>
/// Encapsulated the execute action
/// </summary>
private Action<object> execute;
/// <summary>
/// Encapsulated the representation for the validation of the execute method
/// </summary>
private Predicate<object> canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Initializes a new instance of the RelayCommand class
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommandBase(Action<object> execute)
: this(execute, DefaultCanExecute)
{
}
/// <summary>
/// Initializes a new instance of the RelayCommand class
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommandBase(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;
}
#endregion // Constructors
#region ICommand Members
/// <summary>
/// An event to raise when the CanExecute value is changed
/// </summary>
/// <remarks>
/// Any subscription to this event will automatically subscribe to both
/// the local OnCanExecuteChanged method AND
/// the CommandManager RequerySuggested event
/// </remarks>
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
this.CanExecuteChangedInternal += value;
}
remove
{
CommandManager.RequerySuggested -= value;
this.CanExecuteChangedInternal -= value;
}
}
/// <summary>
/// An event to allow the CanExecuteChanged event to be raised manually
/// </summary>
private event EventHandler CanExecuteChangedInternal;
/// <summary>
/// Defines if command can be executed
/// </summary>
/// <param name="parameter">the parameter that represents the validation method</param>
/// <returns>true if the command can be executed</returns>
public bool CanExecute(object parameter)
{
return this.canExecute != null && this.canExecute(parameter);
}
/// <summary>
/// Execute the encapsulated command
/// </summary>
/// <param name="parameter">the parameter that represents the execution method</param>
public void Execute(object parameter)
{
this.execute(parameter);
}
#endregion // ICommand Members
/// <summary>
/// Raises the can execute changed.
/// </summary>
public void OnCanExecuteChanged()
{
EventHandler handler = this.CanExecuteChangedInternal;
if (handler != null)
{
//DispatcherHelper.BeginInvokeOnUIThread(() => handler.Invoke(this, EventArgs.Empty));
handler.Invoke(this, EventArgs.Empty);
}
}
/// <summary>
/// Destroys this instance.
/// </summary>
public void Destroy()
{
this.canExecute = _ => false;
this.execute = _ => { return; };
}
/// <summary>
/// Defines if command can be executed (default behaviour)
/// </summary>
/// <param name="parameter">The parameter.</param>
/// <returns>Always true</returns>
private static bool DefaultCanExecute(object parameter)
{
return true;
}
}
三、ViewModel 中属性和方法的写法
1.属性
public string LoginID
{
get { return this.loginModel.LoginID; }
set
{
this.loginModel.LoginID = value;
RaisePropertyChanged(() => LoginID);
}
}
2.方法
private ICommand _LoginCommand;
public ICommand LoginCommand
{
get
{
if (this._LoginCommand == null)
{
this._LoginCommand = new RelayCommand(LoginAction);
}
return this._LoginCommand;
}
}
3.snippet
![我常用的两个文件](https://img-blog.csdnimg.cn/20190429081445894.png)
四、ViewModel范例
using LoginDemo1.Models;
using System;
using System.Windows;
using System.Windows.Input;
namespace LoginDemo1.ViewModels
{
public class LoginViewModel : NotificationObject, ILoginViewModel
{
private LoginModel loginModel = new LoginModel();
#region UI驱动 事件
/// <summary>
/// 登陆成功,交由UI处理的事件
/// </summary>
public event EventHandler LoginSuccess;
/// <summary>
/// 登陆失败,交由UI处理的事件
/// </summary>
public event TipsEventHandler LoginFailed;
/// <summary>
/// 退出事件
/// </summary>
public event EventHandler LoginViewClose;
#endregion
#region ICommand方法
private ICommand _LoginCommand;
public ICommand LoginCommand
{
get
{
if (this._LoginCommand == null)
{
this._LoginCommand = new RelayCommand(LoginAction);
}
return this._LoginCommand;
}
}
private ICommand _LoginOutCommand;
public ICommand LoginOutCommand
{
get
{
if (this._LoginOutCommand == null)
{
this._LoginOutCommand = new RelayCommand(LoginOutAction);
}
return this._LoginOutCommand;
}
}
private void LoginOutAction(object obj)
{
var handler= this.LoginViewClose;
if (handler != null) handler(this, EventArgs.Empty);
}
private void LoginAction(object parameter)
{
if (this.LoginID != "admin" || this.Password != "123")
{
OnLoginFailed("用户名或密码错误!\n正确的用户名是admin\n正确的密码是123");
return;
}
OnLoginSuccess();
}
protected virtual void OnLoginSuccess()
{
var handler = this.LoginSuccess;
if (handler != null)
handler(this, EventArgs.Empty);
}
protected virtual void OnLoginFailed(string tips)
{
var handler = this.LoginFailed;
if (handler != null)
handler(this, new TipsEventArgs(tips));
else//如果事件没有被外部注册,调用默认方法
MessageBox.Show(tips);
}
#endregion
#region 属性
public string LoginID
{
get { return this.loginModel.LoginID; }
set
{
this.loginModel.LoginID = value;
RaisePropertyChanged(() => LoginID);
}
}
public string Password
{
get { return this.loginModel.Password; }
set
{
this.loginModel.Password = value;
RaisePropertyChanged(() => Password);
}
}
#endregion
}
}