在经典MVVM设计中使用BindableBase

时间:2015-09-30 13:17:47

标签: c# wpf mvvm

我对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用于指定的设计并获得一些代码改进?

由于

4 个答案:

答案 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

,都无关紧要