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 RelayCommand(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 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;
        }

        #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

    }
}

猜你喜欢

转载自blog.csdn.net/cxb2011/article/details/89667535