2011-12-13 104 views
0

我有一个数据网格,它绑定到实现INotifyPropertyChanged的Item对象。MVVM Datagrid从视图和模型更新

在ViewModel中,我订阅了来自更新Item对象的外部设备服务的更改。 datagrid是可编辑的,因此Item也可以从View中更改。该值应写入设备,但尚未在视图中更新,因为设备写入可能会失败。如果成功,设备将发出一个我已经订阅的事件。

我的一些担忧是。

我在哪里可以通过ViewModel或Item对象在设备服务上调用写入? 如何确保数据网格中显示的值在编辑后“恢复”,直到从设备接收事件?

的几点思考

  1. 如果它的项目对象,然后Item对象不再是DTO,而是一个ViewModel我猜。所以我会为同一视图(用户控件)提供两种视图模型。一个用于用户控件,另一个用于数据网格中的项目。这不符合我对视图模型的理解。但也许这是错的? 然后Item如何知道值是从视图(由用户)还是由视图模型(由设备服务)更新?

  2. ViewModel订阅Item对象上的PropertyChanged。要检测值是否从视图中更改,ViewModel可以从服务中取消订阅PropertyChanged或设置标志。它似乎很笨重,但会起作用。 也许我应该做两个属性:ViewValue和ServiceValue。 ViewModel应该更新ServiceValue并订阅ViewValue,它可以在阅读ViewValue之后将ViewValue还原为ServiceValue。

  3. 视图处理CellEditEnding并通知视图模型

回答

2

关于1点):是的,使用视图模型绑定对UI,而不是DTO。 这是帮助从MVVM中的视图中分离数据的主要思想。

至于点2)和3),我建议你在你的ViewModels上实现IEditableObject接口,可能在一个公共基类上。

该界面提供了方法BeginEdit(), CancelEdit(), and EndEdit()。通过使用这些方法,您可以清楚而可读地控制何时修改哪个对象以及何时提交这些更改,例如到服务或数据库。

此外,实现这些方法可以为您提供一种机制来处理像“ViewValue”和“ServiceValue”这样的不同数据版本。当调用方法BeginEdit()时,可以创建ServiceValue的本地副本作为回退,如果调用方法CancelEdit(),则调用此本地回退值。如果调用EndEdit(),那么这是将值提交回服务的时间,并且在成功之后将当前值存储在该本地临时值中作为新的回退值。

编辑以添加点1回答)

我不知道这是否是“最佳实践”,但至少这是我们在工作中会英寸

我们通常以这样一种方式组织ViewModel,即每个视图都有一个ViewModel,它包含特定视图的所有信息。我们称之为“主ViewModel”。如果我们需要一个视图层次结构,我们将实现遵循相同层次结构的ViewModel层次结构。

在像您这样的情况下,我们会将DataGrid放入另一个包含在主视图中的UserControl。主ViewModel将保存另一个ViewModel,它再次保存DataGrid所需的数据。这个ViewModel将被设置为DataGrid-UserControl的DataContext。这样你仍然保持一个干净的one-ViewModel-per-View分离。

然后可以配置DataGrid中的数据的其他格式,数值为Templates

+0

IEditableObject听起来不错,我会看看它。关于第1点,也许我还不够清楚。我担心的是,对于“相同”视图,我可能有两个视图模型,一个用于窗口,另一个用于窗口中数据网格的项目。我在想一个viewmodel属于一个window/usercontrol,而不是一个数据网格中的项目。但也许我的想法是错误的? – Karsten

+0

您也可以实现'你们哪绑定到'DataGrid'实体名单IEditableObject'接口。它会给你更多的灵活性。 –

+0

Hmm interresting将DataGrid放入用户控件中。我喜欢清晰的名字,所以我想知道你是如何命名用户控件和视图模型的? – Karsten

1

1)让您的DTO类由您的外部数据服务更新将不会进入视图模型。您的DTO是您的模型或业务对象层的一部分。它的工作是在演示文稿(视图和视图模型)和存储层(数据库)之间传输数据,并执行验证(如果需要)。由外部(非视图)服务进行更新是其职责范围内的内容。你对每个窗口或者用户控件的一个虚拟机的评论是没有错的 - 虚拟机是在每个视图上而不是按照控制的基础上分配的(尽管可以为多个视图重用相同的虚拟机类)。您应该拥有视图在VM上绑定的DTO实例。这是虚拟机的工作 - 组织,管理并向视图呈现模型的必需部分。

为了让DTO知道更新来自哪里,就像您在问题中提到的一样,您可以添加一个标志来指示它是来自视图还是服务,并根据需要进行设置。或者,您可以使服务通过公共方法更新DTO,并让视图直接绑定到属性。根据你的设计,这个选项可能比设置一个标志更不可行。

2)IEditableObject,正如在其他答案中提到的那样,是一个不错的选择。在DTO上备份服务和视图数据是不必要的,并且可以有效地将对象的大小增加三倍(假设它是一个很薄的包装器,如果它是DTO,它应该是这样)。如果你需要恢复到任一版本的数据的能力 - 比如说,因为你给了用户选择恢复时使用哪个版本的选项 - 将额外的数据保留在虚拟机上而不是模型上。我想不出有什么理由需要不涉及某种形式的演示或用户交互决策的选项。数据驱动的还原应该始终是确定性的。

3)再次,IEditableObject。该观点不应该通知视图模型;它应该是无知的虚拟机(没有任何引用)。这将放松两层之间的耦合并增加应用程序的灵活性。我通常通过拥有专门处理视图的控制器类来实现这一点。当它打开一个视图时,它会创建一个新的VM实例,并将视图的点号DataContext指向它。