简介
我有一个应用程序可以在运行时导入实验室仪器数据。导入此数据,然后按照最终用户根据其测试要求设置的间隔在ListView
中显示。当感兴趣的值出现在他们观察的ListView
中时,他们会按下“开始”按钮,应用程序开始对该数据和后续数据执行计算,直到按下“停止”按钮。因此,屏幕左侧是一个用于显示导入数据的视图,右侧是用于在计算和显示值时查看值和统计信息的另一个视图。
当前代码
显示导入数据的ListView的视图是ImportProcessView.xaml,它将DataContext
设置为ImportProcessViewModel.cs
。我刚刚介绍的VM有一个属性ObservableCollection<IrData>
,ListView,我刚刚描述,绑定到。现在到了有趣的部分......
ImportProcessView
有ContentControl
动态设置其内容的UserControl,表示特定于最终用户选择的阶段类型的控件和字段。
<StackPanel Background="White" Margin="5">
<ContentControl Content="{Binding CurrentPhaseView}"/>
</StackPanel>
有三个PhaseViews
,每个都在自己的用户控件中,每个DataContext
都设置为ImportProcessViewModel
。结果我得到了一些严重的VM膨胀到2000行。荒谬。我知道。膨胀的原因是因为ImporProcessViewModel
通过三个PhaseView中的每一个的属性来维护状态,不仅如此,还包含执行计算的方法,这些计算的数据存储并显示在这些“PhaseViews”中。
我想要实现的目标
显然,在ImportProcessViewModel
变得更加笨拙之前,我需要将其分解,以便每个PhaseView都有自己的ViewModel,但每个ViewModel都会维护一个与ImportProcessViewModel关系的关系,以便依赖于IrData
的ObservableCollection。
的R&amp; d
我已经完成了对ViewModels相互通信的研究,但大多数results涉及使用特定MVVM框架编写的应用程序。我没有使用框架,在项目的这一点上,重构它以开始使用框架为时已晚。
然而,我确实发现了这个article并且'hbarck'提供的答案提示了一些简单的组合来实现我想要的结果,但是因为我没有太多使用DataTemplates的经验所以我没有了解他/她建议将“UserControl的ViewModel作为主ViewModel上的属性公开,并将ContentControl绑定到此属性,然后通过DataTemplate实例化View(即UserControl)”/ EM>
具体来说,我不明白将ContentControl绑定到此属性是什么意思,然后通过DataTemplate实例化 。
有人可以通过代码示例澄清在此示例的上下文中通过DataTemplate实例化视图的含义吗?
此外,这是一个好方法(正如'hbarck'所建议的那样)?
可以看出,我已经将ContentControl的Content属性设置为要实例化的Phase View。我只是不知道DataTemplate会是什么样的。
答案 0 :(得分:4)
我不明白当他/她建议揭露“的时候是什么意思 UserControl的ViewModel作为主ViewModel上的属性,并绑定 一个ContentControl到这个属性,然后实例化 通过DataTemplate“
查看(即UserControl)
DataTemplate
允许您指定视图(例如用户控件)和视图模型之间的关系。
<DataTemplate DataType="{x:Type myApp:MyViewModel}">
<myApp:MyUserControl />
</DataTemplate>
这会告诉ContentPresenter
只要将其内容属性设置为MyUserControl
的实例,就会显示MyViewModel
。视图模型将用作用户控件DataContext
。通常,DataTemplate
会添加到您的应用程序资源中。
该答案的作者所说的是,您可以拥有一个viewModel,该viewModel具有另一个viewModel类型的属性,该属性绑定到Content
的{{1}}属性。
ContentPresenter
假设您<ContentPresenter Content="{Binding ParentViewModel.ChildViewModelProperty}"/>
指定了DataTemplate
与您的用户控件之间的关系,WPF会自动将用户控件加载到您的视图中。
This answer我提供给另一个问题也可能会为您提供一些帮助。
我需要将其分解,以便每个PhaseView都有自己的ViewModel, 而且每个ViewModel都保持一种关系 ImportProcessViewModel。
这将允许您将viewModel分解为更小,更易于管理的自定义视图模型。这将使您在viewModels之间进行通信时遇到问题。
如果按照建议嵌套viewModel,那么您的子viewModel可以公开父viewModel可以绑定的事件,以便在发生更改时通知它。像这样:
ChildViewModel
这很简单,不需要任何其他框架。有些人可能反对这种方法,但它是有效的解决方案。缺点是viewModels必须知道彼此存在才能订阅事件,因此最终可能会紧密耦合。您可以使用标准的面向对象设计原则来解决这个问题(I.E.从接口派生您的子viewModel,以便父级只知道接口而不是实现)。
如果您真的想要进行松耦合通信,那么您需要使用某种事件聚合或消息总线系统。这与上述方法类似,只是有一个对象位于视图模型之间并充当中介,因此viewModel不必知道彼此存在。我的answer here提供了更多信息。
已有预先存在的解决方案,但这需要采用额外的框架。我建议使用Josh Smiths MVVM foundation因为它很简单,你只需要使用一个类。
答案 1 :(得分:3)
虽然本杰明的答案非常精细且非常有用,但我想澄清一下我在其他帖子中写的内容如何适用于你的问题:
<DataTemplate DataType="{x:Type my:Phase1VM}">
<my:Phase1View/>
</DataTemplate>
<DataTemplate DataType="{x:Type my:Phase2VM}">
<my:Phase2View/>
</DataTemplate>
<DataTemplate DataType="{x:Type my:Phase3VM}">
<my:Phase3View/>
</DataTemplate>
请注意,没有x:Key,只有DataType值。如果这样声明,当被要求分别显示Phase1VM,Phase2VM或Phase3VM类型的对象时,WPF将选择适当的DataTemplate。 Phase1View,Phase2View和Phase3View将是UserControls,它将知道如何显示不同的ViewModel。他们不会自己实例化他们的ViewModel,但期望他们的DataContext从外部设置为各自ViewModel的实例。
假设应在主视图中声明应显示阶段视图的ContentControl,并且DataContext将是主ViewModel,您将声明ContentControl,如下所示:
<ContentControl Content="{Binding CurrentPhaseVM}"/>
根据CurrentPhaseVM的实际类型,这将选择三个DataTemplates中的一个,并显示相应的UserControl。 UserControl的DataContext将自动成为ContentControl的内容,因为这将导致选择DataTemplate的对象。
编辑:列表和代码格式不会合在一起,似乎......