C#MVVM:添加新的ViewModel(严格的非暴露模型设计)



我一直在使用C#中的MVVM应用程序,但是在使用ViewModels的集合时会遇到一些问题。具体而言,它们都倾向于与模型是ViewModel的私人成员有关。

一个例子是创建新的ViewModels(按视图的要求)。对于某些序言(尽管您可能不需要这些来帮助我),这是示例模型和ViewModel类:

Private Class Model()
{
    public string Name { get; set; }
}
Public Class ViewModel()
{
    Private Model _Model;
    Public Void ViewModel(Model model)
    {
        _Model = model;
    }
    Public String Name
    { 
        get
        {
            return _Model.Name;
        }
        set
        {
            _Model.Name = value;
        }
    }
}

整个模型永远不会直接作为ViewModel的公共成员暴露。MainWindowViewModel处理模型的集合(私有,视图看不见这些)和ViewModels(公共供查看消化):

Public Class MainWindowViewModel
{
    Private List<Model> _NamesModel;
    Private ObservableCollection<ViewModel> _NamesViewModel;
    Public Void MainWindowViewModel()
    {
        //Lets pretend we have a service that returns a list of models
        _NamesModel = Service.Request();
        foreach(Model model in _NamesModel)
        {
            ViewModel viewmodel = new ViewModel(model);
            _NamesViewModel.Add(viewmodel);
        }
    }
    Public ObservableCollection<ViewModel> NamesViewModel
    {
        get
        {
            return _NamesViewModel;
        }
    }
}

现在,这是序言,但现在我有一个问题。如何添加新的ViewModel?我视图中的方法会创建新的ViewModel并填充它吗?作为纯粹主义者,我假设不应完全允许创建或填充模型。我的ViewModel是否应该包含一个不接受的构造函数(即没有基础模型),而是创建一个空白以填充?

这些问题不断提出"纯" MVVM方法。我必须在我的ViewModel(Bool比较(模型模型))中创建一个公共方法,该方法将将模型(准备删除等)与内部使用比较。如果这些模型是公开暴露的(断开纯度),那么做一些事情要容易得多,例如找到与模型连接的ViewModel。

我可以同情其中一些问题。我最近写了一个MVVM应用程序,其中经常出现类似的问题。诀窍之一是确定哪个类将负责模型实例。您是否希望它成为您的MainWindowViewModel?还是您的nameViewModel?您不想分享在这两个类之间创建/删除模型的职责;您将有一场后勤的噩梦。

其次,即使是"纯" MVVM方法,也不能规定您不能公开展示该模型。您说这样做会为您节省很多头痛:做到这一点。MVVM仅规定ViewModel没有视图的知识/访问。有许多"官方" MVVM示例,甚至使用InotifyPropertychanged接口实现其模型,并直接与模型上的属性结合。

就我个人而言,我认为我将控制名称ViewModel的Namemodel控制。这意味着您应该从MainWindowViewModel完全删除Namemodels的列表。如果您想将nameViewModel一个可选的构造函数提供模型,那也很好。

我是这种方法的粉丝:

public NameViewModel : ViewModelBase
{
    public NameModel Model
    { 
        get { /* get stuff */ }
        set { /* set stuff */ }
    } 
    // Default constructor creates its own new NameModel
    public NameViewModel()
    {
        this.Model = new NameModel();
    }
    // Constructor has a specific model dictated to it
    public NameViewModel(NameModel model)
    {
        this.Model = model;
    }
    //Model wrapper properties
    public String Name
    { 
        get { return Model.Name; }
        set { Model.Name = value; }
    }
}

和...

public class MainWindowViewModel
{
    Private ObservableCollection<ViewModel> _NameViewModels;
    Public Void MainWindowViewModel()
    {
        //Lets pretend we have a service that returns a list of models
        var nameModels = Service.Request();
        foreach(Model model in nameModels)
        {
            ViewModel viewmodel = new NameViewModel(model);
            NameViewModel.Add(viewmodel);
        }
    }
    Public ObservableCollection<ViewModel> NameViewModels
    {
        get
        {
            return _NameViewModels;
        }
    }
}

以这种方式,您的MainWindowViewModeldoes不能保留模型的完全独立的副本;它仅跟踪nameViewModels。每个名称ViewModel负责其自己的基础模型,同时仍然可以使该选项在施工期间使用A 特定模型传递给它。

所有与创建相关的问题都可以通过引入工厂设计模式来解决。工厂将照顾创建查看模型基于提供的模型。

public class MainWindowViewModel
{
    private List<Model> _NamesModel;
    private ObservableCollection<ViewModel> _NamesViewModel;
            private IViewModelFactory factory;
    public void MainWindowViewModel(IViewModelFactory factory)
    {
        //Lets pretend we have a service that returns a list of models
        _NamesModel = Service.Request();
        _NamesViewModel = factory.CreateNamesViewModels(_NamesModel);
    }
    public ObservableCollection<ViewModel> NamesViewModel
    {
        get
        {
            return _NamesViewModel;
        }
    }
}

更重要的是,您甚至可以摆脱视图模型中的Service依赖关系并将其移至工厂本身,从而减少了将模型保持在视图模型中的需求(尽管如此,否则,删除模型可能在更复杂的方案中无法使用):

public ObservableCollection<ViewModel> CreateNamesViewModels()
{
    var models = Service.Request();
    return new ObservableCollection(models.Select(m => new ViewModel(m)));
}

另外,您的主窗口视图模型可以揭示使用工厂创建任何新实例的commands。这样,没有模型泄漏以查看,也不会暴露创建细节(因为命令将隐藏实际实现)。

最新更新