如果你不知道里面发生了什么,那么使用 MVVM 框架毫无价值。
让我们一步一步来构建你自己的 ViewModelBase 类。
ViewModelBase 是所有视图模型的通用类。让我们将所有常见的逻辑移到这个类中。
-
你的 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;}
}
请注意,不再推荐使用 OnPropertyChanged(() => SomeProperty),因为我们在 C# 6 中有 nameof 运算符。
-
通常的做法是像这样实现调用 PropertyChanged 的属性:
public string FirstName
{
get { return _firstName; }
set { SetProperty(ref _firstName, value); }
}
让我们在视图模型库中定义 SetProperty:
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
它只是在属性值更改并返回 true 时触发 PropertyChanged 事件。当值未更改并返回 false 时,它不会触发事件。基本思想是,SetProperty 方法是虚拟的,您可以在更具体的类中扩展它,例如触发验证,或通过调用 PropertyChanging 事件。
这很漂亮。这是您的 ViewModelBase 在此阶段应包含的所有内容。其余的取决于您的项目。例如,您的应用程序使用基于页面的导航,并且您编写了自己的 NavigationService 来处理来自 ViewModel 的导航。因此,您可以将 NavigationService 属性添加到您的 ViewModelBase 类中,这样您就可以根据需要从所有视图模型中访问它。
为了获得更多的可重用性并保持 SRP,我有一个名为 BindableBase 的类,它几乎是我们在这里所做的 INotifyPropertyChanged 的实现。我在每个 WPF/UWP/Silverligt/WindowsPhone 解决方案中重复使用这个类,因为它是通用的。
然后在每个项目中创建从 BindableBase 派生的自定义 ViewModelBase 类:
public abstract ViewModelBase : BindableBase
{
//project specific logic for all viewmodels.
//E.g in this project I want to use EventAggregator heavily:
public virtual IEventAggregator () => ServiceLocator.GetInstance<IEventAggregator>()
}
如果我有使用基于页面导航的应用程序,我还会为页面视图模型指定基类。
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<DialogButton> DialogButtons { get; }
public bool? DialogResult
{
get { return _dialogResult; }
set { SetProperty(ref _dialogResult, value); }
}
public void Close()
{
Closing?.Invoke(this, EventArgs.Empty);
}
}