【问题标题】:MVVM Datagrid updated from View and Model从视图和模型更新的 MVVM 数据网格
【发布时间】:2012-01-19 05:51:04
【问题描述】:

我有一个数据网格,它绑定到一个实现 INotifyPropertyChanged 的​​ Item 对象。

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

我的一些担忧是。

我应该从 ViewModel 还是从 Item 对象调用设备服务上的 write? 如何确保数据网格中显示的值在编辑后“还原”,直到接收到来自设备的事件?

一些想法

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

  2. ViewModel 订阅 Item 对象的 PropertyChanged。要检测 View 的值是否已更改,ViewModel 可以在从服务获取事件时取消订阅 PropertyChanged 或设置标志。它看起来很笨拙,但会起作用。 也许我应该创建两个属性:ViewValue 和 ServiceValue。 ViewModel 应该更新 ServiceValue 并订阅 ViewValue,它可以在读取 ViewValue 后将其恢复为 ServiceValue。

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

【问题讨论】:

    标签: .net wpf mvvm datagrid


    【解决方案1】:

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

    关于第 2) 点和第 3) 点,我建议您在 ViewModel 上实现IEditableObject 接口,可能在通用基类上。

    此接口提供方法BeginEdit(), CancelEdit(), and EndEdit()。使用这些方法可以让您清晰易读地控制何时修改哪个对象以及何时提交这些更改,例如到服务或数据库。

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

    编辑以添加第 1 点的答案)

    我不确定这是否是“最佳实践”,但至少这是我们在工作中的做法。

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

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

    DataGrid 中数据的其他格式可能随后配置为 va Templates

    【讨论】:

    • IEditableObject 听起来不错,我去看看。关于第1点,也许我还不够清楚。我担心我可能有两个用于“相同”视图的视图模型,一个用于窗口,一个用于窗口中数据网格中的项目。我在想视图模型属于窗口/用户控件,而不是数据网格中的项目。但也许我的想法是错误的?
    • 你也可以为实体实现IEditableObject接口,你绑定到DataGrid的列表。它会给你更多的灵活性。
    • 嗯,仅将 DataGrid 放入用户控件中很有趣。我喜欢清晰的名称,所以我想知道您如何命名用户控件和视图模型?
    【解决方案2】:

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

    要让 DTO 知道更新的来源,正如您在问题中提到的,您可以添加一个标志来指示它是来自视图还是服务并根据需要进行设置。或者,您可以让服务通过公共方法更新 DTO,并让视图直接绑定到属性。根据您的设计,此选项可能远不如设置标志可行。

    2) IEditableObject,正如另一个答案中提到的,是一个不错的选择。在 DTO 上备份服务和视图数据是不必要的,并且有效地使对象的大小增加了三倍(假设它是一个瘦包装器,如果它是一个 DTO 则应该如此)。如果您真的需要能够恢复到任一版本的数据 - 例如,因为您让用户可以选择在恢复时使用哪个版本 - 保留额外的VM 上而不是模型上的数据。我想不出你需要不涉及某种演示或用户交互决策的选项的原因。数据驱动的还原应设计为始终具有确定性。

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

    【讨论】:

      猜你喜欢
      • 2011-01-29
      • 2012-08-06
      • 2014-08-04
      • 2018-06-30
      • 1970-01-01
      • 1970-01-01
      • 2019-01-19
      • 2015-06-14
      • 1970-01-01
      相关资源
      最近更新 更多