我对BindableBase类的使用以及如何将这种“新”机制应用于经典MVVM设计感到困惑。
简而言之,问题如下:当我们在视图模型类中引用模型时,如何正确使用BindableBase类?
详细信息:
经典MVVM模式:查看< - > View-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;
}
...
}
让我们想象一下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方法进行此类设计,也是调用raw onPropertyChanged方法的唯一方法。
似乎SetProperty是BindableBase类的最大好处,而且我们不能在MVVM的这种直接和通用实现中使用它是非常奇怪的。所以我想也许我错过了某些东西或者对指定的课程做错了。
您是否知道如何将BindableBase用于指定的设计并获得一些代码改进?
由于
答案 0 :(得分:2)
目前,您的ViewModel
向您的Model
公开View
个属性。这是罚款但是如果你的Model
有很多需要暴露的属性,那就太荒谬了。您能想象必须为具有20多个属性的Model
创建属性吗?
相反,您应该使用Model
中的属性将View
公开给ViewModel
。
public MyClass Model { get; private set; }
注意:这也可以实现INotifyPropertyChanged
。
Model
中的属性应该实现INotifyPropertyChanged
,或者在您的情况下BindableBase
。
public class MyClass : BindableBase
然后您的View
可以直接绑定到Model
属性。这可能看起来像你打破了设计模式,但事实并非如此,你的View
仍然对你的Model
一无所知,但它只是假设它的属性是什么?期待,因此你的课程仍然脱钩。
唯一的缺点是,Model
现在取决于BindableBase
,这不是世界末日,但如果你处于可以&#39的情况; t 修改Model
类,然后您的当前方法即可。
答案 1 :(得分:1)
像 Mike Eason 所说,公开你的模型是好的,ViewModel的主要目标之一就是为你的视图准备模型。也就是说,我倾向于只为自己的只读视图公开模型。
您可以从BindableBase继承并创建一个方法,允许您以与字段相同的方式更改Model的属性。
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;
}
}
isValueNew Func用于确定值是否不同。然后您可以像下面这样使用它:
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.
}
}
}
}
这是我能想到的最简单的方法来实现你的目标。
答案 2 :(得分:0)
我会保持你的主题属性的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)
}
}
}
答案 3 :(得分:0)
试一试:
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)
}
}
}
<强> UPD 强>
我决定在第一次评论后添加一些解释。
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
。