WPF/MVVM:将域模型集合委托给 ViewModel
域模型集合(通常是 List 或 IEnumerable)委托给 ViewModel。
这意味着我的 CustomerViewModel 有一个 List 或 IEnumerable 类型的订单集合。
绑定控件无法识别列表中的任何更改。但对于 ObservableCollection 来说确实如此。
这是MVVM设计模式中的一个问题。
你如何应对?
更新:我的做法示例:
public class SchoolclassViewModel : ViewModelBase
{
private Schoolclass _schoolclass;
private ObservableCollection<PupilViewModel> _pupils = new ObservableCollection<PupilViewModel>();
public SchoolclassViewModel(Schoolclass schoolclass)
{
_schoolclass = schoolclass;
_schoolclass.Pupils = new List<Pupil>();
foreach (var p in schoolclass.Pupils)
Pupils.Add(new PupilViewModel(p));
}
public Schoolclass GetSchoolclass
{
get { return _schoolclass; }
}
public int ID { get; set; }
public string SchoolclassName
{
get { return _schoolclass.SchoolclassName;}
set
{
if(_schoolclass.SchoolclassName != value)
{
_schoolclass.SchoolclassName = value;
this.RaisePropertyChanged("SchoolclassName");
}
}
}
public ObservableCollection<PupilViewModel> Pupils
{
get{ return _pupils;}
set
{
_pupils = value;
this.RaisePropertyChanged("Pupils");
}
}
}
A domain model collection (normally a List or IEnumerable) is delegated to a ViewModel.
Thats means my CustomerViewModel has a order collection of type List or IEnumerable.
No change in the list is recognized by the bound control. But with ObservableCollection it is.
This is a problem in the MVVM design pattern.
How do you cope with it?
UPDATE: Sample of how I do it:
public class SchoolclassViewModel : ViewModelBase
{
private Schoolclass _schoolclass;
private ObservableCollection<PupilViewModel> _pupils = new ObservableCollection<PupilViewModel>();
public SchoolclassViewModel(Schoolclass schoolclass)
{
_schoolclass = schoolclass;
_schoolclass.Pupils = new List<Pupil>();
foreach (var p in schoolclass.Pupils)
Pupils.Add(new PupilViewModel(p));
}
public Schoolclass GetSchoolclass
{
get { return _schoolclass; }
}
public int ID { get; set; }
public string SchoolclassName
{
get { return _schoolclass.SchoolclassName;}
set
{
if(_schoolclass.SchoolclassName != value)
{
_schoolclass.SchoolclassName = value;
this.RaisePropertyChanged("SchoolclassName");
}
}
}
public ObservableCollection<PupilViewModel> Pupils
{
get{ return _pupils;}
set
{
_pupils = value;
this.RaisePropertyChanged("Pupils");
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我通过不按照你描述的方式来处理这个问题。
如果我需要在视图中呈现一个
Foo
对象及其相关的Bar
对象,FooViewModel
通常会实现一个BarsObservableCollection
类型的 code> 属性。请注意,这与底层
Foo
类是否具有IEnumerable
类型的Bars
属性无关。Foo
类可能不会。应用程序甚至可能不需要能够迭代Foo
的所有Bar
对象(UI 中除外)。编辑
当我的视图是应用程序对象模型的简单表示时,我所做的事情几乎与您在示例中所做的一样。我的构造函数中的代码通常更紧凑一些:
但本质上是一样的。
但这假设 Foo 实际上公开了 Bars 属性。可能不会。或者也许只有一些
Bar
对象应该出现在视图中。或者它们可能应该与其他对象混合在一起,并且 FooViewModel 应该公开某种类型的 CompositeCollection。我要表达的观点是视图模型是视图的模型。这不一定与底层对象模型有直接对应。
举一个简单的例子:我的程序可以为用户提供一种将项目放入五个不同类别的方法,方法是将它们拖放到五个不同的
ListBox
控件中。最终,这样做会在Item
对象上设置一个Category
属性。我的视图模型将有一个CategoryViewModel
对象的集合,每个对象都有一个ObservableCollection
类型的属性,因此在集合之间来回拖动项目很容易实施。问题是,应用程序的对象模型中甚至可能没有
Category
类,更不用说Category
对象的集合了。Item.Category
可能只是string
类型的属性。CategoryViewModel
并未镜像应用程序的对象模型。它的存在只是为了支持 UI 中的视图。I deal with this by not doing it the way you describe.
If I need to present a
Foo
object and its relatedBar
objects in the view, theFooViewModel
will generally implement aBars
property of typeObservableCollection<BarViewModel>
.Note that this is irrespective of whether or not the underlying
Foo
class has aBars
property of typeIEnumerable<Bar>
. TheFoo
class might not. The application might not even need to be able to iterate over all of theBar
objects for aFoo
, except in the UI.Edit
When my view is a simple representation of the application's object model, I pretty much do things as you do in your sample. The code in my constructor is generally a bit more compact:
but it's essentially the same thing.
But this assumes that
Foo
actually exposes aBars
property. It might not. Or maybe only someBar
objects should appear in the view. Or maybe they should appear intermingled with other objects, and theFooViewModel
should expose aCompositeCollection
of some kind.The point I'm making is that the view model is a model of the view. This doesn't necessarily have a direct correspondence to the underlying object model.
To pick a simple example: My program may give the user a way of putting items into five different categories by dragging and dropping them into five different
ListBox
controls. Ultimately, doing this sets aCategory
property on theItem
object. My view model is going to have a collection ofCategoryViewModel
objects, each with a property of typeObservableCollection<ItemViewModel>
, so that dragging items back and forth between collections is simple to implement.The thing is, there may not even be a
Category
class in the application's object model, let alone a collection ofCategory
objects.Item.Category
might just be a property of typestring
. TheCategoryViewModel
isn't mirroring the application's object model. It only exists to support the view in the UI.好吧,我会继续添加我的想法作为答案,而不是在评论中。 :)
我认为最重要的是,这就是 WPF 和数据绑定工作方式的现实。为了进行双向数据绑定,集合需要一种通知绑定到它们的控件的方法,而大多数域对象中使用的标准列表和集合不/不会/不应该支持这一点。正如我在评论中提到的,要求为非集合属性实现 INotifyPropertyChanged 是标准域对象可能无法满足的另一个要求。
域对象并不打算成为视图模型,因此您可能会发现需要在两种类型的对象之间来回映射。这与必须在域对象和数据访问对象之间来回映射没有什么不同。每种类型的对象在系统中都有不同的功能,并且每种对象都应该专门设计以支持它们自己在系统中的角色。
综上所述,Agies 使用 AOP 自动生成代理类的想法非常有趣,也是我打算研究的内容。
Ok, I'll go ahead and add my thoughts as an answer instead of in the comments. :)
I think the bottom line is that this is just the reality of the way WPF and databinding work. In order for two-way databinding to operate, collections need a means of notifying controls that are bound to them, and the standard lists and collections used in most domain objects don't/won't/shouldn't support this. As I mentioned in a comment, being required to implement INotifyPropertyChanged for non-collection properties is another requirement that may not be met by a standard domain object.
Domain objects are not intended to to be viewmodels, and for this reason you may find that you need to map back and forth between the two types of objects. This is not dissimilar to having to map back and forth between domain objects and data access objects. Each type of object has a different function in the system, and each should be specifically designed to support their own role in the system.
All that said, Agies's idea of using AOP to automatically generate proxy classes is very interesting, and something I intend to look into.
我所做的不是在域模型中使用 ObservableCollection,而是使用我自己的集合类型来实现 INotifyCollectionChanged 接口以及其他有用的标准和自定义接口。我的想法很像 Rockford Lhotka 在他的书中建议的那样,更改通知的用途不仅仅是只是表示层,因为当另一个对象的状态发生变化时,域层中的其他业务对象通常需要某种通知。
使用这种方法,您可以创建自己的集合类型,该集合类型仍然具有更改通知以及您需要的自定义行为的优点。集合的基类可以实现为纯粹的基础设施代码,然后可以使用 这本书。因此,最终您可以拥有一个可以包装 IEnumerable<> 类型的集合。并为您的域模型和演示代码提供您正在寻找的更改通知内容。
What I do is instead of using ObservableCollection in my domain model is use my own collection type that implements the INotifyCollectionChanged interface amongst other useful standard and custom interfaces. My way of thinking is that much like Rockford Lhotka suggests in his book that change notification is useful in to more than just a presentation layer since other business objects within the domain layer often need some sort of notification when state changes in another object.
With this methodology you could create your own collection type that still has the benefits of change notification and as well as what ever custom behaviors you need. The base class for your collection could be implemented as purely infrastructure code and then a subclass could be created that could contain business logic using the subtype layering techinque used in this book. So in the end you could have a collection that can wrap types of IEnumerable<> and provide the change notification stuff your looking for as well for both your domain model and presentation code.