wpf - WPF MVVM : an issue of organising ViewModels

标签 wpf xaml mvvm

我认为我面临的是设计问题-我认为这个问题已经被很多人解决了很多次,因为这似乎很普遍。

我将WPF与XAML和简单的MVVM方法结合使用,以供记录。

我的意图是使用WPF中的MVVM设计模式创建TreeView。

我有一个包含两个类的数据模型:场景和角色。每个场景都包含许多角色。

我创建了一个CharacterViewModel,它非常简单(并且运行良好)。自然地,这将环绕现有的Character类。

Scene类让我感到困惑。据我了解,SceneViewModel应该环绕Scene类,就像CharacterViewModel对Character类所做的一样。但是不同之处在于,场景包含角色列表,因此增加了exta的复杂性。

这两个选项似乎如下:

选项1:场景包含角色列表,因此SceneViewModel也将其作为它的一部分。

选项2:场景包含CharacterViewModel的列表,因此SceneViewModel也将其作为它的一部分。

老实说,我不确定该去哪一个。我怀疑这是第二个(而且this教程似乎同意(示例6是我所指的部分的标题)。第一个选项似乎会使事情真的很奇怪(以及为什么还要创建CharacterViewModel?)但是第二个似乎很奇怪,因为在程序的模型部分应该包含什么以及在程序的 View 模型部分应该包含什么,这似乎使人感到困惑。

我希望我已经解释了我的问题,也希望有人可以提供一些帮助。

谢谢。

最佳答案

首先让我谈谈这一声明:

...the SceneViewModel should wrap around the Scene class, just as the CharacterViewModel did for the Character class.



这不是真的。应该为每个 View 创建 View 模型。您的模型类可能一对一,但这并不是MVVM想法的严格部分。一个 View 可能需要显示来自多个“根”模型元素(与您的应用程序中的父子关系没有显式关系的模型元素)的数据,或者您可能需要为一个模型元素拥有多个 View 。并且进一步详细说明,理想情况下,每个 View 模型应尽可能与 View 技术隔离(即,单个 View 模型足以创建WinForms View 或WPF View 或HTML View 等)。

例如,您可能有一个显示Scene类中的数据的 View 。该 View 还可能显示Character中每个Scene的数据。用户可能能够单击Character并打开仅针对该Character的 View (例如,弹出窗口)。在这种情况下,可能会有单独的 View 模型来表示根 View 和弹出窗口中的Character。我倾向于根据 View 的根来命名 View 模型类。对于像您这样的应用程序,我会有类似SceneViewModelSceneCharacterViewModel(或SceneViewModel_CharacterCharacterInSceneViewModel -这些名称中的任何一个都表明该类用于在Character的 View 中表示Scene)。这将使该 View 模型与弹出 View (以Character为中心,并被命名为CharacterViewModel(甚至是CharacterDialogViewModelCharacterPopupViewModelCharacterEditorViewModel)类似。

使模型与 View 模型之间的集合保持同步很烦人,但通常是必要的。请注意,并非总是必要的-在某些情况下,您会发现不需要向模型添加其他 View 模型功能,因此在这种情况下 View 直接引用模型是完全可以接受的。

保持模型集合和 View 模型集契约(Contract)步的示例:假设您的根SceneView的每个Character都有一个按钮。该按钮将显示Character的弹出窗口。进一步假设Character弹出窗口没有类似的按钮,因为这样它将允许弹出窗口打开另一个弹出窗口(等)。您可能需要使用ICommand实现,以便可以将按钮绑定(bind)到命令。 ICommand实例绝对不在模型中(即使命令可能在模型上调用公共(public)方法)。适当的位置应该在Character View 中的Scene的 View 模型中(而不是弹出窗口中Character的 View 模型中)。对于模型中的每个Character,您都需要创建一个引用Character的 View 模型并存储其他 View 模型内容(ICommand对象)。

这意味着,随着Character的添加/从Scene中删除,您需要专门为Character View 模型中的Scene创建 View 模型。我通常会这样做:
  • 在构造时(或 View 模型最初接收模型的任何时间),为每个子对象创建一个 View 模型。将这些 View 模型放入带有ReadOnlyCollection<SceneCharacterViewModel>之类的公共(public)属性中。您的 View 将绑定(bind)到该集合。
  • 在将子对象添加到模型中时(内部或通过模型上的公共(public)方法),模型应以适当的方式通知 View 模型。由于模型不应该直接引用 View 模型(甚至不通过接口(interface),模型也应该完全起作用,即使在没有 View 模型的非UI上下文中也是如此),最合适的方法是使用事件。您可以通过以下两种方法执行此操作:
  • 公开模型中的事件,例如CharacterAddedCharacterRemoved甚至CharactersUpdated(这些事件的最后一个将能够使用单个事件传达添加或删除的信息)
  • View 模型中最常用的
  • ObservableCollection(或ReadOnlyObservableCollection)也可以在模型中使用,在这种情况下,所有事件都已经可用。缺点是处理这些集合类型的事件并不是最简单的事情。
  • 完全不同的第三个选项:如果 View 模型或命令实例直接调用sceneModel.AddCharacter(newCharacterModel)之类的方法,则只需在此行之后立即更新 View 模型,而无需任何事件。我经常因为这种方法很简单而开始使用这种方法,但是我几乎总是总是使用前两种技术之一,因为这些技术即使在内部进行更新的情况下,也允许模型通知 View 模型(例如,在内部进行更新)。对定时事件或模型控制的异步操作的响应)。

  • 综上所述,这就是您的应用程序看起来像“纯” MVVM架构的样子。纯度可能是以简单为代价的,因此有时最好在这里和那里走一些捷径。一个常见的快捷方式:在WPF中,仅将所有子窗口小部件内容放在用来显示 child 的ItemTemplateItemsControl中,而不是为 child 创建单独的UserControl,通常会更容易。

    enter image description here

    关于wpf - WPF MVVM : an issue of organising ViewModels,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42566082/

    相关文章:

    wpf - ObservableCollection 中的更改不会更新 ListView

    c# - 动态数据网格,可选列

    c# - WPF 使用 DataBinding 根据其值设置标签背景

    wpf - MVVM 和事件

    c# - WPF 如何从 ViewModel 访问控件

    c# - 将数据从数据层获取到 View 中的 observablecollection

    c# - WPF:使用 MVVM 时更改数据绑定(bind)

    python - 使用 WPF 控件和 Ironpython 打开文件对话框并选择一个文件

    WPF ListView 列标题对齐

    c# - 导航回 View 时是否有用于处理 View 模型的良好模式?