如何在MVVM(WPF)中编写“ViewModelBase”

我是WPF编程环境中的新手。 我正在尝试使用MVVM设计模式编写程序。

我做了一些研究并阅读了一些与之相关的文章,很多时候我遇到过这个问题

ViewModelBase

我知道它是什么..但是我可以具体知道我应该从哪里开始写出我自己的ViewModelBase吗? 就像……真正了解正在发生的事情而不会变得太复杂。 谢谢 :)

如果您不知道内部发生了什么,那么使用MVVM框架是没有价值的。

因此,让我们一步一步地构建您自己的ViewModelBase类。

  1. ViewModelBase是所有视图模型的通用类。 让我们将所有常见逻辑移到这个类。

  2. 你的ViewModel应该实现INotifyPropertyChanged (你明白为什么吗?)

     public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } 

    [CallerMemberName]属性不是必需的,但它允许你写: OnPropertyChanged(); 而不是OnPropertyChanged("SomeProperty"); ,因此您将避免代码中的字符串常量。 例:

     public string FirstName { set { _firtName = value; OnPropertyChanged(); //instead of OnPropertyChanged("FirstName") or OnPropertyChanged(nameof(FirstName)) } get{ return _firstName;} } 

    请注意,不建议使用nameof OnPropertyChanged(() => SomeProperty) ,因为我们在C#6中有nameof运算符。

  3. 通常的做法是实现调用PropertyChanged的属性,如下所示:

     public string FirstName { get { return _firstName; } set { SetProperty(ref _firstName, value); } } 

    让我们在viewmodelbase中定义SetProperty:

     protected virtual bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = "") { if (EqualityComparer.Default.Equals(storage, value)) return false; storage = value; this.OnPropertyChanged(propertyName); return true; } 

    它只是在属性值更改并返回true时触发PropertyChanged事件。 当值未更改并且返回false时,它不会触发事件。 基本思想是, SetProperty方法是虚拟的,您可以在更具体的类中扩展它,例如触发validation,或者通过调用PropertyChanging事件。

这很漂亮。 这是您在此阶段应包含的所有ViewModelBase。 其余的取决于你的项目。 例如,您的应用程序使用页面基本导航,并且您已编写自己的NavigationService以处理来自ViewModel的导航。 因此,您可以将NavigationSerivce属性添加到ViewModelBase类,因此,如果需要,您可以从所有视图模型中访问它。

为了获得更多的可重用性并保持SRP,我有一个名为BindableBase的类,它几乎是我们在这里完成的INotifyPropertyChanged的实现。 我在每个WPF / UWP / Silverligt / WindowsPhone解决方案中重用此类,因为它是通用的。

然后我在每个项目中创建从BindableBase派生的自定义ViewModelBase类:

 public abstract ViewModelBase : BindableBase { //project specific logic for all viewmodels. //Eg in this project I want to use EventAggregator heavily: public virtual IEventAggregator () => ServiceLocator.GetInstance() } 

如果我有应用程序,它使用基于页面的导航我也为页面视图模型指定基类。

 public abstract PageViewModelBase : ViewModelBase { //for example all my pages has title: public string Title {get; private set;} } 

我可以有另一个用于对话的类:

 public abstract DialogViewModelBase : ViewModelBase { private bool? _dialogResult; public event EventHandler Closing; public string Title {get; private set;} public ObservableCollection DialogButtons { get; } public bool? DialogResult { get { return _dialogResult; } set { SetProperty(ref _dialogResult, value); } } public void Close() { Closing?.Invoke(this, EventArgs.Empty); } } 

你有一些nuget包来实现MVVM

  1. MVVM灯
  2. MVVM Cross
  3. 棱镜

对我来说,初学者更容易的是MVVM灯,因为它提供了一些代码示例。

因此,更好的方法是安装此nuget包,查看生成的代码,如果需要,请回复我们以获取更多解释。

我喜欢这个BaseVewModel,它为你的视图模型提供了一个很好的干净风格。 查看各种“之前”和“之后”比较。 当然,没有什么是强制性的 – 如果您不喜欢BaseViewModel提供的function,那么请不要使用它。 或者修改它,因为你有源代码。 特别要注意的是,有三种不同的方法可以通过更改通知实现属性 – 选择您理解/感觉舒适的复杂程度。

在大多数MVVM框架中,基本ViewModel类实际上只包含很少的代码 – 通常只是INotifyPropertyChanged和一些辅助函数的实现。

看一下MVVM Light的ViewModelBase和ObservableObject类的源代码。 ObservableObject主要是INotifyPropertyChanged实现 – 使用lambda表达式而不是属性名称的“magic strings”。 ViewModelBase扩展了ObservableObject,并且主要是一种实用工具方法,用于确定您是否在Visual Studio设计器中运行

这里有一个很好的讨论:关于这个问题的https://codereview.stackexchange.com/q/13823 。 采用表达式的好方法,以便在提升属性更改通知时获得类型安全性。