MVVM正确的事件流

时间:2017-10-05 19:47:48

标签: c# wpf events mvvm

我对MVVM有些新意,但我已经掌握了一段时间了。不幸的是,仔细阅读互联网似乎没有就应用程序处理MVVM事件的最佳方式达成一致意见。我有一个如下图所示的情况,其中我将两个控件组合在一起 - 每个控件都在一个自包含的xml中,我将调用两个单独的视图:ListBox和ButtonPanel。

MasterView

事件的方向应该如何流动呢?让我们看一个场景,在按钮面板中单击我的按钮后,将在列表框控件中创建一个文件。图像一或图像二中的事件流是否更合适?

事件流程一 - 将ViewModel传递给ViewModel: enter image description here

事件流程二 - 所有前端类都不通信。所有消息都通过应用层传递(无论连接到后端的是什么:db,处理函数等等。) enter image description here

请注意 - 我没有想象它,但我使用ICommand将点击事件从我的按钮面板视图传递到我的按钮面板视图模型。

最后我的问题是:如何在MVVM流程中处理事件。我的图形是否合适?

* **编辑回应Ed Plunkett的答案 ***

这是您期望的那种事件流程吗? enter image description here

这是否更接近正确的架构?

在此图像中<< >> ViewModel之间实际上是暴露给视图的属性,并且大多数事件在视图中作为绑定处理。

我对此的担忧是:

  1. Master ViewModel现在负责从两个视图中收集信息。我不确定如何在不使用mediator / messenger的情况下处理这个问题 - 这是没有将我的父视图模型紧密耦合到子视图。我必须在所有视图模型中按名称注册事件,或者为每个视图模型按名称进行特定回调,因此仍然需要调解器来实现松散耦合设计(这是使用MVVM的全部优势)。

  2. 即使我在第1点错了 - 使用父子ViewModel关系有什么好处?

  3. 关于ViewModel的观点,我不知道按钮或面板是什么:

    我同意。你可以在这里忽略我的类命名。我这样做是为了让问题易于理解 - 命名类,以便旁观者知道每个控件的处理方式。我的实际类是根据它们处理的数据命名的,而不是组件。

2 个答案:

答案 0 :(得分:0)

最简单的情况:一个视图模型。

该按钮调用从viewmodel的属性获取的命令。

viewmodel也有类似

的属性
public ObservableCollection<SomeFileViewModelClass> Files { /* INPC stuff */ }

该按钮不知道命令的作用。据推测,该命令会告诉viewmodel创建一个文件,并将SomeFileViewModelClass的新实例添加到Files

与此同时,回到XAML,ListBox正在使用Files作为项目来源:

<ListBox
    ItemsSource="{Binding Files}"
    ...

...所以列表框会自动更新以显示新文件。

如果您希望您的按钮面板拥有自己的视图模型(您几乎肯定不会,即使您这样做,也不要将其称为&#34;按钮面板视图模型&#34 ;; viewmodels不知道哪些按钮面板甚至是)和你的文件列表有自己的viewmodel(也许),制作主要的那些东西 child viewmodels 视图模型。主视图模型创建它们并管理它们之间的关系。

public FilesViewModel FileList { /* INPC stuff */ }

也许FileList现在拥有ObservableCollection<SomeFileViewModelClass> Files - 但如果它拥有所有,那么它就不是一个视图模型。只需将Files属性放在主视图模型上即可。

<Button Content="Create File" Command="{Binding CreateFileCommand}" />

<!-- ... snip -->

<ListBox ItemsSource="{Binding FileList.Files}" />

如果CreateFileCommand存在,那么FilesViewModel应该是INotifyPropertyChanged.PropertyChanged的属性。

尽量保持简单,但并不简单。

不要设计UI,然后为UI中的每个事物创建一个viewmodel。那是倒退。控件没有视图模型。

设计视图模型以模拟应用程序需要处理的事物。父视图和子视图模型,基于实际内容之间的实际关系(个人而言,我更喜欢模拟最小的&#34;叶节点&#34;首先是事物,然后以我的方式进入中心)。然后添加视图以将其显示给用户。这需要一点点习惯,但它会得到回报。

您的观看模式是&#34;该计划&#34; 。您编写视图以向用户显示程序并让用户与程序通信。你编写模型/数据库/文件访问/任何让程序存储和检索状态。

您的&#34;所有消息都通过应用层&#34;想法不是MVVM。

你的第二件事是黄色&#34;调解员/信使&#34;框接近正确,但没有&#34; mediator / messenger&#34 ;;父母跟孩子说话。如果一个孩子需要了解一个兄弟姐妹的事情,你可以通过让孩子们揭露事件来解决这个问题。父母可以在孩子之间连接事件处理程序 - 为了快速和肮脏,你可以随时处理孩子的CustomerID, Month-Date, Column 1, Column 2, Column 3 1223, AUG 2017, 10, 100, 1,100 #The last value is 1100 and not 100 1224, AUG 2017, 10, 1, 2 1225, AUG 2017, 2,110, 10, 13 #the first value is 2100 and not 2 ,但通常首选专门的事件。依赖注入是另一种选择,但很容易最终编写带有迷宫依赖的类。 DI可以变成反模式,用户界面中信息流的多色框图是它的主要指标。

答案 1 :(得分:-1)

您的案例已经简化,但您确实拥有&#34;列表框视图&#34;它包含一个ListBox和一个&#34; Button View&#34;包含按钮。这两个视图都是&#34;主窗口视图&#34;。

的子视图

我不得不在这里不同意Ed,并说我永远不会合并虚拟机......这打破了MVVM尝试安装的单一责任范式。

让VM进行通信的标准MVVM方式是通过messenger / event聚合器范例。

因此,我投票支持工作流程#1。概...

虚拟机不应该向另一个虚拟机发送消息以写入数据库等。这是什么型号的。

如果要抽象出数据库,那么标准的方法是定义一个IDataProvider类型的接口,并使用DI将其注入需要它的虚拟机中。当你的应用变得越来越大,越来越复杂时,你会发现DI和MVVM在天堂是一场比赛。

MVVM + DI + Messenger全部协同工作,保持一切独立和有条理。

我个人喜欢保持V&lt; - &gt;虚拟机1:1没有代码,但这是MVVM的超纯粹定义。

编辑:只是为了澄清,我不认为你真的是说你只是在一个表格上放两个控件并使每个控件成为一个VM,我不会这样做。将UI分解为逻辑&#34;区域&#34;和每个&#34;地区&#34;会有一个VM和一个V。