MVVM针对集合的应用

5

我最近开始学习wpf,并尝试使用mvvm。

我的理解是,在mvvm中,视图和模型都不应该知道对方的存在。

我想要做的是在屏幕上显示客户列表。但是如果我按照下面所示的viewModel编写代码,这与我在网上看到的许多示例类似,那么我最终会得到一些像这样的代码:

class Customer 
{    
    public String Name {get;set;}     
    public String Address {get;set;} }
}

class MainWindowViewModel
{
    ObservableCollection<Customer> customers = new ObservableCollection<Customer>();

    public ObservableCollection<Customer> Customer 
    {
      get {return customers;}
    } 

    public MainWindowViewModel() 
    {
     //cust1 and cust2 are Customer objets
      customers.Add(cust1);
      customers.Add(cust2);
    }
}

现在,如果我创建一个MainWindowViewModel实例并将其设置为我的MainWindowView(我的视图)的数据上下文,并且进一步将视图模型的Customers属性绑定到listBox,则视图将需要引用包含我的Models的程序集。
所以我的问题是:
1)在MVVM中添加对Models程序集的引用是否允许?因为这意味着视图知道模型。
2)更好的解决方案是将每个Customer对象封装在CustomerViewModel中,并使MainWindowViewModel包含ObservableCollection of CustomerViewModel而不是ObservableCollection of Customer。这将完全将模型与视图分离。
5个回答

3
  1. 我不确定为什么您认为包含视图的项目需要引用您的模型项目?您的视图中没有直接引用模型的内容 - 您在XAML绑定表达式中使用的仅是名称,而且这些表达式链接的是视图模型的属性,而不是模型的属性。
  2. 如果您的视图需要比模型提供的更多数据,并且不希望更改模型,则将模型包装在视图模型中是一个好选择。例如,您的视图可能需要显示User类型的Age,但User只有一个DateOfBirth属性。创建一个具有Age属性的UserViewModel是一个很好的选择,如果您不想更改您的模型。

当我通过代码将视图的DataContext设置为viewModel时,它会显示找不到类Customer。 - CAA
为什么会这样?客户是您正在实例化的CustomerViewModel的依赖项吗? - devdigital
不,Customer只是一个POCO。ViewModel有一个ObservableCollection<Customer>。当你…… - CAA
我不是很理解,你说你正在将数据上下文设置为视图模型类型。那么,为什么你需要引用一个仅包含你的模型类型的程序集呢? - devdigital
啊,你说得对。是的,我只需要看一遍就能理解它的工作原理了。现在很清楚了,谢谢。 - CAA
显示剩余5条评论

2

以下是您的问题的答案:

  1. View 引用 Model 有什么不好?当它简化代码时,这是完全可以的。反过来 (Model -> View) 是不好的做法。

  2. 当您没有特殊需求时,您不需要将每个 Customer 对象都包装在一个 CustomerViewModel 中。我建议遵循务实的方式,保持代码简洁。

您可能会对 WPF 应用程序框架 (WAF)BookLibrary 示例应用程序感兴趣,该应用程序展示了您在此处描述的场景。


1

MVVM模式与其他MVx模式(MVC,MVP等)类似,鼓励关注点分离(SoC),从而提高代码的可维护性/可测试性。除了通常的SoC外,MVVM还提供以下功能:

  1. 视图逻辑的单元测试;这是因为您将逻辑从视图移动到视图模型中,使视图尽可能“愚笨”。
  2. 开发人员和设计师之间的工作流程;因为视图是“愚笨”的,所以更容易使用XAML而不需要背后的逻辑。

关于可见性,即什么对什么可见,严格如下:

Model <= ViewModel <= View

换句话说,ViewModel 可以看到 Model,但 Model 不能看到 ViewModel。同样地,View 可以看到 ViewModel,但反之不行。
由于 ViewModel 没有对 View 的引用,它使得你的代码可以在没有任何视图组件的情况下执行,这实现了上述第一点。
你的 ViewModel 的目的是“塑造”你的 Model,以便更容易地将其绑定到 View 上。如果你的 View 很简单,那么做以下操作是完全可以接受的:
Model <= View

这仍然允许(1)单元测试,(2)开发者-设计师工作流程。

同时,使用混合方法也是可以的,有时将您的模型暴露给视图,有时将其包装在ViewModel中。例如:

http://www.scottlogic.co.uk/blog/colin/2009/08/the-mini-viewmodel-pattern/

请不要因为认为必须而创建大量样板式的ViewModel代码!

0
通常我们会创建一个CustomerViewModel。这是由我们的通用CollectionViewModelBase类强制执行的。这确保了用户界面使用的每个部分都是专门为显示而创建的,而我们在经常可序列化的POCO模型中没有任何与UI相关的代码。

0
你肯定会想要将你的模型包装在类似下面的视图对象中:
/// <summary>
/// Business model object : Should be in your separate business model only library
/// </summary>
public class BusinessModelObject
{
    public string Prop1 { get; set; }
    public int Prop2 { get; set; }
}



/// <summary>
/// Base notifying object : Should be in your GUI library
/// </summary>
public abstract class NotifyingObject<T> : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, e);
    }


    private static readonly PropertyChangedEventArgs ModelPropertyChanged = new PropertyChangedEventArgs("Model");
    private T _model;
    public T Model
    {
        get { return _model; }
        set
        {
            _model = value;
            NotifyPropertyChanged(ModelPropertyChanged);
        }
    }
}

/// <summary>
/// Model decorator : Should be in your GUI library
/// </summary>
public class BusinessModelObjectAdapter : NotifyingObject<BusinessModelObject>
{
    public BusinessModelObjectAdapter(BusinessModelObject model)
    {
        this.Model = Model;
    }

    private static readonly PropertyChangedEventArgs Prop1PropertyChanged = new PropertyChangedEventArgs("Prop1");
    private string _prop1;
    public string Prop1
    {
        get { return Model.Prop1; }
        set
        {
            Model.Prop1 = value;
            NotifyPropertyChanged(Prop1PropertyChanged);
        }
    }

    private static readonly PropertyChangedEventArgs Prop2PropertyChanged = new PropertyChangedEventArgs("Prop2");
    private int _prop2;
    public int Prop2
    {
        get { return Model.Prop2; }
        set
        {
            Model.Prop2 = value;
            NotifyPropertyChanged(Prop1PropertyChanged);
        }
    }

    //and here you can add whatever property aimed a presenting your model without altering it at any time
}

那么,您是否需要创建另一个类来存储 BusinessModelObjectAdapter 实例的 ObservableCollection。因为最终我要将对象列表绑定到 GUI。 - CAA
不需要为另一个类创建:在您的 ViewModel 中,您将公开一个“ObservableCollection<BusinessModelObjectAdapter>”,而不是一个“ObservableCollection<BusinessModelObject>”。 - Bruno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接