MVVMLight:

使用NUGET安装MVVMLight包后,会自动生成ViewModel文件夹,里面包含一个ViewModelLocator,在这个文件中,使用依赖注入的方式,添加对ViewModel的注册,然后在.xaml文件中引入。这样做的好处,

1:一个view可以注册多个viewmodel,

2:从.XAML文件中引入,在界面会及时显示出绑定的数据,友好界面调整。

3:切换页面的时候,数据可以缓存的,因为在ViewModelLocator中,ServiceLocator.Current.GetInstance(class)这个方法是通过单例模式注册获取的。就是说,ViewModel类中定义的属性值是一直存在。

如果是在.cs文件中通过  DataContext=new xxxViewModel();关联ViewModel类,每次都会new一个新的初始化的类。

 public class ViewModelLocator
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            ////if (ViewModelBase.IsInDesignModeStatic)
            ////{
            ////    // Create design time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DesignDataService>();
            ////}
            ////else
            ////{
            ////    // Create run time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DataService>();
            ////}
            SimpleIoc.Default.Register<MainViewModel>();
            SimpleIoc.Default.Register<WelcomeViewModel>();
            SimpleIoc.Default.Register<BothWayBindViewModel>();
        public MainViewModel Main
                return ServiceLocator.Current.GetInstance<MainViewModel>();
        public WelcomeViewModel Welcome
                return ServiceLocator.Current.GetInstance<WelcomeViewModel>();
        public BothWayBindViewModel BothWayBind
                return ServiceLocator.Current.GetInstance<BothWayBindViewModel>();
        public static void Cleanup()
            // TODO Clear the ViewModels
View Code
<Window x:Class="WPF_MVVMLight.BothWayBindView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF_MVVMLight"
         DataContext="{Binding Source={StaticResource Locator},Path=BothWayBind}"
        mc:Ignorable="d"
        Title="BothWayBindView" Height="450" Width="800">
    <Window.Resources>
View Code

实现INotifyPropertyChanged接口的基类

 public abstract class ViewModelBase : INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// Sets property if it does not equal existing value. Notifies listeners if change occurs.
        /// </summary>
        /// <typeparam name="T">Type of property.</typeparam>
        /// <param name="member">The property's backing field.</param>
        /// <param name="value">The new value.</param>
        /// <param name="propertyName">Name of the property used to notify listeners.  This
        /// value is optional and can be provided automatically when invoked from compilers
        /// that support <see cref="CallerMemberNameAttribute"/>.</param>
        protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string propertyName = null)
            if (EqualityComparer<T>.Default.Equals(member, value))
                return false;
            member = value;
            OnPropertyChanged(propertyName);
            return true;
        /// <summary>
        /// Notifies listeners that a property value has changed.
        /// </summary>
        /// <param name="propertyName">Name of the property, used to notify listeners.</param>
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
View Code

model绑定

 public class SelectableViewModel : ViewModelBase
        private bool _isSelected;
        private string _name;
        private string _description;
        private char _code;
        private double _numeric;
        private string _food;
        private string _visibility;
        public bool IsSelected
            get => _isSelected;
            set => SetProperty(ref _isSelected, value);
        public string Visibility
            get => _visibility;
            set => SetProperty(ref _visibility, value);
        public char Code
            get => _code;
            set => SetProperty(ref _code, value);
        public string Name
            get => _name;
            set => SetProperty(ref _name, value);
        public string Description
            get => _description;
            set => SetProperty(ref _description, value);
        public double Numeric
            get => _numeric;
            set => SetProperty(ref _numeric, value);
        public string Food
            get => _food;
            set => SetProperty(ref _food, value);
View Code

MVVMLight

调用MVVMLight的ObservableObject,自动实现INotifyPropertyChanged,在属性中添加 RaisePropertyChanged(() => “属性名”);

  public class WelcomeModel : ObservableObject
        private String introduction;
        /// <summary>
        /// 欢迎词
        /// </summary>
        public String Introduction
            get { return introduction; }
                introduction = value; RaisePropertyChanged(() => Introduction);
View Code

注意:对于数组集合类的,一定使用ObservableCollection声明(原生和MVVMLight两者都适用)

 public ObservableCollection<MovieCategory> MovieCategories { get; }
  MovieCategories = new ObservableCollection<MovieCategory>
                new MovieCategory("一号货架",
                    new Movie("一排一号", "1001"),
                    new Movie("二排一号", "1002"),
                    new Movie("三排一号", "1003")),
                new MovieCategory("二号货架",
                    new Movie("一排一号", "2001"),
                    new Movie("二排一号", "2002")
View Code

因为ObservableCollection实现了接口Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged,而List没有实现INotifyCollectionChanged, INotifyPropertyChanged。

  [TypeForwardedFrom("WindowsBase, Version=3.0.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
    public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
        public ObservableCollection();
        // 摘要:
        //     Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection`1
        //     class that contains elements copied from the specified list.
        // 参数:
        //   list:
        //     The list from which the elements are copied.
        // 异常:
        //   T:System.ArgumentNullException:
        //     The list parameter cannot be null.
        public ObservableCollection(List<T> list);
        public ObservableCollection(IEnumerable<T> collection);
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        protected event PropertyChangedEventHandler PropertyChanged;
        public void Move(int oldIndex, int newIndex);
        protected IDisposable BlockReentrancy();
        protected void CheckReentrancy();
        protected override void ClearItems();
        protected override void InsertItem(int index, T item);
        protected virtual void MoveItem(int oldIndex, int newIndex);
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e);
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e);
        protected override void RemoveItem(int index);
        protected override void SetItem(int index, T item);
View Code

实现ICommand接口

/// <summary>
    /// No WPF project is complete without it's own version of this.
    /// </summary>
    public class AnotherCommandImplementation : ICommand
        private readonly Action<object> _execute;
        private readonly Func<object, bool> _canExecute;
        public AnotherCommandImplementation(Action<object> execute)
            : this(execute, null)
        public AnotherCommandImplementation(Action<object> execute, Func<object, bool> canExecute)
            if (execute is null) throw new ArgumentNullException(nameof(execute));
            _execute = execute;
            _canExecute = canExecute ?? (x => true);
        public bool CanExecute(object parameter) => _canExecute(parameter);
        public void Execute(object parameter) => _execute(parameter);
        public event EventHandler CanExecuteChanged
                CommandManager.RequerySuggested += value;
            remove
                CommandManager.RequerySuggested -= value;
        public void Refresh() => CommandManager.InvalidateRequerySuggested();
View Code
 public AnotherCommandImplementation AddCommand { get; }
        public AnotherCommandImplementation RemoveSelectedItemCommand { get; }
            AddCommand = new AnotherCommandImplementation(
                    if (!MovieCategories.Any())
                        MovieCategories.Add(new MovieCategory(GenerateString(15)));
                        var index = new Random().Next(0, MovieCategories.Count);
                        MovieCategories[index].Movies.Add(
                            new Movie(GenerateString(15), GenerateString(20)));
            RemoveSelectedItemCommand = new AnotherCommandImplementation(
                    var movieCategory = SelectedItem as MovieCategory;
                    if (movieCategory != null)
                        MovieCategories.Remove(movieCategory);
                        var movie = SelectedItem as Movie;
                        if (movie == null) return;
                        MovieCategories.FirstOrDefault(v => v.Movies.Contains(movie))?.Movies.Remove(movie);
                _ => SelectedItem != null);
View Code

MVVMLight

using GalaSoft.MvvmLight.CommandWpf; using MVVMLightDemo.Model; using System.Collections.ObjectModel; namespace MVVMLightDemo.ViewModel public class CommandViewModel:ViewModelBase public CommandViewModel() //构造函数 ValidateUI = new ValidateUserInfo(); List = new ObservableCollection<ValidateUserInfo>(); #region 全局属性 private ObservableCollection<ValidateUserInfo> list; /// <summary> /// 用户数据列表 /// </summary> public ObservableCollection<ValidateUserInfo> List get { return list; } set { list = value; } private ValidateUserInfo validateUI; /// <summary> /// 当前操作的用户信息 /// </summary> public ValidateUserInfo ValidateUI get { return validateUI; } validateUI = value; RaisePropertyChanged(() => ValidateUI); #endregion #region 全局命令 private RelayCommand submitCmd; /// <summary> /// 执行提交命令的方法 /// </summary> public RelayCommand SubmitCmd if (submitCmd == null) return new RelayCommand(() => ExcuteValidForm(),CanExcute); return submitCmd; set { submitCmd = value; } #endregion #region 附属方法 /// <summary> /// 执行提交方法 /// </summary> private void ExcuteValidForm() List.Add(new ValidateUserInfo(){ UserEmail= ValidateUI.UserEmail, UserName = ValidateUI.UserName, UserPhone = ValidateUI.UserPhone }); /// <summary> /// 是否可执行(这边用表单是否验证通过来判断命令是否执行) /// </summary> /// <returns></returns> private bool CanExcute() return ValidateUI.IsValidated; #endregion View Code

注意:CanExecuteChanged,CanExecute,Execute的说明

 Messenger消息注册

如果需要某张视图页面弹出对话框、弹出子窗体、处理界面元素,播放动画等。如果这些操作都放在ViewModel中,就会导致ViewModel还是要去处理View级别的元素,造成View和ViewModel的依赖。

最好的办法就是ViewModel通知View应该做什么,而View监听接收到命令,并去处理这些界面需要处理的事情。

      2、ViewModel和ViewModel之间也需要通过消息传递来完成一些交互。 

      而MVVM Light  的 Messenger类,提供了解决了上述两个问题的能力:

Messenger类用于应用程序的通信,接受者只能接受注册的消息类型,另外目标类型可以被指定,用Send<TMessage, TTarget>(TMessage message) 实现,

InitializeComponent();
//消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,并执行相应的处理,所以Sender那边的token要保持一致 //执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,注意这边是支持泛型能力的,所以传递参数很方便。 Messenger.Default.Register<String>(this, "ViewAlert", ShowReceiveInfo); this.DataContext = new MessengerRegisterForVViewModel(); //卸载当前(this)对象注册的所有MVVMLight消息 this.Unloaded += (sender, e) => Messenger.Default.Unregister(this); /// <summary> /// 接收到消息后的后续工作:根据返回来的信息弹出消息框 /// </summary> /// <param name="msg"></param> private void ShowReceiveInfo(String msg) MessageBox.Show(msg); View Code if (sendCommand == null) sendCommand = new RelayCommand(() => ExcuteSendCommand()); return sendCommand; set { sendCommand = value; } private void ExcuteSendCommand() Messenger.Default.Send<String>("ViewModel通知View弹出消息框", "ViewAlert"); //注意:token参数一致 #endregion View Code

比如弹出框,可以在ViewMode中使用,但是需要引入using System.Windows;如果是子页面,就脱了了View和ViewModel解耦的初衷。