BindableBase在经典MVVM设计中的使用
本文关键字:经典 MVVM BindableBase | 更新日期: 2023-09-27 18:15:12
我对BindableBase类的用法以及如何将这种"新"机制应用于经典的MVVM设计有点困惑。
简单地说,问题如下:当我们在视图模型类中引用模型时,如何正确使用BindableBase类?
细节:
经典MVVM模式:View <-> View-Model -> Model
正如我们所看到的,这个方案中的View-Model知道Model,但是Model对View和View-Model一无所知。
如果我们实现这种方法,我们将得到这样的东西:
// Model
class Task
{...}
// View-Model
class TaskViewModel : BindableBase
{
private readonly Task _task;
public TaskViewModel(Task task)
{
_task = task;
}
...
}
让我们想象任务类有'Subject'属性,我们应该显示这个数据。所以根据MVVM,我应该:
创建View-Model中'Subject'属性的副本:
// View-Model
class TaskViewModel : BindableBase
{
public String Subject
{
get{ return _task.Subject; }
set
{
_task.Subject = value;
// I can't use SetProperty(ref _task.Subject, value)
// it's contradict c# syntax
OnPropertyChanged("Subject");
}
}
}
正如你所看到的,我不能使用SetProperty方法来进行这样的设计,唯一的方法是调用原始的onPropertyChanged方法。
似乎SetProperty是BindableBase类的最大优点,但我们不能在MVVM的这种直接和常见的实现中使用它,这是非常奇怪的。所以我想也许我错过了什么或工作不正确的指定类。
你知道如何使用BindableBase为指定的设计和得到一些代码改进吗?
谢谢
当前,您的ViewModel
将Model
属性暴露给View
。这是很好但是它变得相当荒谬,如果你的Model
有很多属性需要暴露。你能想象为一个拥有20多个属性的Model
创建属性吗?
ViewModel
中使用属性将Model
公开给View
。public MyClass Model { get; private set; }
注意:这也可以实现INotifyPropertyChanged
你的Model
中的属性应该实现INotifyPropertyChanged
,或者在你的情况下,BindableBase
。
public class MyClass : BindableBase
你的View
可以直接绑定到Model
属性。这可能看起来像你打破了设计模式,但事实并非如此,你的View
仍然不知道你的Model
,但它只是假设它所期望的属性,因此你的类仍然是解耦的。
唯一的缺点是你的Model
现在依赖于BindableBase
,这不是世界末日,但如果你在一个情况下,你不能修改Model
类,那么你目前的方法是走的路。
就像Mike Eason说的,公开你的模型是可以的,ViewModel的主要目标之一就是让模型为你的视图做好准备。也就是说,我倾向于只为只读视图公开模型。
你可以继承BindableBase并创建一个方法来允许你像修改字段一样修改模型的属性。
public class ViewModelBase : BindableBase
{
protected bool SetProperty(
Func<bool> isValueNew,
Action setValue,
[CallerMemberName] string propertyName = null)
{
if (isValueNew())
{
return false;
}
setValue();
OnPropertyChanged(propertyName);
return true;
}
}
isvaluennew函数用于确定值是否不同。然后,您可以像下面这样使用它:
public class MyViewModel : ViewModelBase
{
private readonly MyModel myModel = new MyModel();
public string Name
{
get { return myModel.Name; }
set
{
if (SetProperty(() => myModel.Name == value, () => myModel.Name = value))
{
// Do something here since the value was changed.
}
}
}
}
这是我能想到的最简单的方法来实现你所追求的。
我会让Subject属性的setter保持简单。
考虑先从你的模型中加载数据,然后将数据分配给你的视图模型属性。
class TaskViewModel : BindableBase
{
LoadData()
{
Subject = GetSubject(); // Relies on model
}
string _subject = null;
public String Subject
{
get{ return _subject; }
set
{
_subject = value;
SetProperty(ref _task.Subject, value)
}
}
}
试一试:
class TaskViewModel : BindableBase
{
private readonly Task _task;
private string _subject;
public TaskViewModel(Task task)
{
_task = task;
Subject = _task.Subject;
}
public string Subject
{
get { return _subject; }
set
{
_task.Subject = value;
SetProperty(ref _subject, value)
}
}
}
乌利希期刊指南
我决定在第一个评论之后添加一些解释。
BindableBase只是实现INotifiPropertyChanged
接口的类的基本版本。这是SetProperty
方法的实现(用于Prism
框架):
public abstract class BindableBase : INotifyPropertyChanged
{
// Some other members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals((object) storage, (object) value))
return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
}
可以看出,方法SetProperty
只是"语法糖",OnPropertyChanged
在里面执行。因此,调用SetProperty
或OnPropertyChanged
方法并不重要。