如何使用MVVM架构处理集合?

25

我不太明白在涉及列表/集合时如何应用MVVM模式。

假设MainModel有几个属性和方法,以及一个包含其他DetailModel对象的列表。 DetailModel对象可以添加、删除或重新排序。

MainView将显示与根模型相关的几个控件,并从列表中填充ListBox。每个项目都将通过DetailModelView UserControl拥有自己的子视图。

最后,有一个MainViewModel。这具有由MainModel的属性和方法支持的属性,绑定到主视图,并使用更改通知保持所有内容同步(到此为止,我对该模式感到满意 - 只是说明除非我缺少了某些基本东西...)

当涉及处理列表时,我会感到困惑。我已经遇到过几个例子,其中MainViewModel仅将DetailModels列表公开给视图,DetailModelViews直接绑定到模型。 这是可行的,但存在问题。 它不一致地遵循该模式(没有DetailViewModel),并且它迫使我在我的详细模型中包含一些与UI相关的代码。 对我来说很清楚,MainViewModel应该公开一个DetailViewModels列表供UI绑定,但是我不知道如何实现这样一件事!

如何管理两个列表(DetailModelsDetailViewModels)? 我真的不知道初始填充DetailViewModel列表的位置以及如何处理添加、删除或更改项目顺序以保持它们同步!

4个回答

12
通常,Models仅仅是数据对象,它们不应该包含任何添加/删除列表项等操作的代码。这是ViewModel的工作。
在您的情况下,我建议创建一个MainViewModel,其具有以下属性:
  • ObservableCollection<DetailViewModel> Details
  • ICommand AddDetailCommand
  • ICommand RemoveDetailCommand
如果您的MainModel类是一个数据对象,您可以将其公开,也可以从MainViewModel中公开它的属性。公开其属性是"MVVM纯粹主义者"的方法,而公开整个模型有时更加实用。
您的MainViewModel负责创建初始的DetailViewModels列表,并负责添加/删除这些项。例如,在MainViewModel.MainModel属性的PropertyChanged事件中,它可能会重建MainViewModel.Details集合,并且MainViewModel.Details属性的CollectionChanged事件将更新MainViewModel.MainModel.Details

1
这个相当简洁地描述了最终的解决方案。我的“MainViewModel”还具有将所选项目向上或向下移动的命令... - mbmcavoy

6

你说得没错,应该分别有一个DetailModels列表和DetailViewModels列表。 DetailViewModels列表应该是一个类型为ObservableCollection<DetailViewModel>的属性。 当设置模型时(或在ViewModel的构造函数中传递模型时),可以填充可观察列表。

private ObservableCollection<DetailViewModel> m_details;
public IEnumerable<DetailViewModel> Details
{
   get { return m_details; }
}

你可以订阅m_details。CollectionChanged。这是你可以在模型中处理列表内容重新排序的地方。
希望这能帮到你。

3

根据我的经验,只有在进行简单的只读展示时,例如在 ComboBox 中显示字符串属性时,您才能使用模型对象暴露给视图。 如果涉及任何实际的涉及对象的 UI(特别是涉及双向数据绑定的 UI),则需要使用视图模型。

通常,主 VM 的构造函数如下所示:

public MasterViewModel(MasterModel m)
{
   _Model = m;
   _Detail = new ObservableCollection<DetailViewModel>(m.Detail);
}

其中MasterModel.Detail是一个DetailModel对象的集合,_Detail是用于公开给视图的Detail属性的后备字段。

至于在此列表中添加、删除和重新排序项目,至少在UI上,这将通过MasterViewModel上的命令完成,该命令必须操作MasterModel.DetailMasterViewModel.Detail。虽然有点麻烦,但除非您想在每次更改MasterModel.Detail后重新填充MasterViewModel.Detail,否则这是不可避免的。

另一方面,如果您一直在想“我为什么需要为视图模型编写单元测试?”,现在您知道了。


我仍在努力解决我的问题,但我想指出一个很大的帮手:“在每次更改MasterModel.Detail后重新填充MasterViewModel.Detail”。奇怪的是,这个想法没有出现在我的脑海中!我肯定必须在初始化时构建它一次,以便随时可以重建。 - mbmcavoy
2
我在想 new ObservableCollection<DetailViewModel>(m.Detail); 是否能够工作,因为 m.Detail 的类型与通过ObservableCollection泛型指定的 <DetailViewModel> 类型不同。我怀疑它不会工作。 - marc wellman

1

这里有一个我认为非常好地解决了这个问题的答案,使用了一个 ObservableViewModelCollection<TViewModel, TModel>

它很方便。构造函数中需要一个ObservableCollection和一个ViewModelFactory。我喜欢它,因为它将状态保留在模型层,这是它应该存在的地方。GUI上的用户操作可以调用VM上的命令,通过M上的公共方法来操纵M。任何在M层上产生的更改都将由此链接中的类自动处理。

https://stackoverflow.com/q/2177659/456490

请注意我的关于SL与WPF的评论。

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