mvvm - 如何在一个窗口中创建 VM 之间的 MVVM 父/子关系?

标签 mvvm parent-child

我正在开发一个一次性应用程序,但我喜欢利用这些机会来提高我对不熟悉事物的技能。所以我决定使用 MVVM 和 WPF,而不是坚持我的 WinForms 舒适区。我不知道如何让我的 UI 的某些部分相互通信。好吧,这更像是我找不到一种看起来不太可靠的方法。

应用程序测试一种算法的性能,该算法用于从大量收集数据中确定切片的最小值/最大值。 Here's what it looks like.

主窗口的 ViewModel 生成一个包含许多值的集合。我想为切片指定一个范围,然后执行几个版本的最小/最大算法来验证正确性和执行时间。

底部组框中的部分是 UserControl 的两个实例,我将其称为子控件。这对我来说很有意义,因为我知道我需要几个并且不想在主窗口中复制/粘贴集群。这就是问题发生的地方。

主窗口 VM 具有集合和切片范围的属性。我需要子虚拟机才能访问这些属性。事实证明这很困难。理想情况下,我想要这样的东西:

<local:AlgorithmTester RangeStart="{Binding RangeStart}"
                       RangeEnd="{Binding RangeEnd}"
                       Values="{Binding Values}" />

子 VM 有自己的 VM。如果我将依赖属性放在控件上,我还必须将这些属性链接到控件的 VM,可能通过代码内绑定(bind)。这对我来说似乎有点不可思议。我想到了以下替代方案,但对我来说似乎都很奇怪:
  • 不要使用用户控件;复制/粘贴每个测试人员的代码。
  • 在代码中为测试人员设置 VM,使 lambda 可以在闭包中捕获所需的属性。 (现在就这样做。)这让我觉得很脏,因为 lambdas 应该属于另一个类。
  • 一位 Twitter friend 建议使用事件聚合器。子虚拟机将订阅“RangeStartChanged”聚合事件(和其他事件)。我不喜欢这样,因为我觉得子虚拟机和父虚拟机之间的关系应该更清楚;也许它的旧习惯很难死?
  • 对具有适当属性的类使用模板化的 ItemsControl,而不是 UserControl。我仍然必须找到一种方法来传达主要 VM 属性中的更改,但这将是主 VM 内部的逻辑。我不喜欢这种方法的唯一原因是它让我觉得很奇怪。这可能是多年的 WinForms 经验让我望而却步。 (越想这个越喜欢。)

  • 还有其他解决方案吗?我对现有的那些太挑剔了吗?你会怎么办?

    最佳答案

    我会让您的 ParentViewModel 包含 ChildViewModel,并使用 DataTemplate 告诉 WPF 它应该使用 local:AlgorithmTester 绘制 ChildViewModel控制

    示例 View 模型

    public class ParentViewModel
    {
        // Actual implementation omitted for sake of simplicity
        public ChildViewModel TestViewModelA { get; set; }
        public ChildViewModel TestViewModelB { get; set; }
    }
    

    示例 XAML
    <Window>
        <Window.Resources>
            <!-- Data Template to tell WPF how to draw the ViewModels -->
            <DataTemplate DataType="{x:Type local:ChildViewModel}">
                <local:AlgorithmTester />
            </DataTemplate>
        <Window.Resources>
    
        <!-- Put ViewModels in ContentControls and let WPF figure out how to display them -->
        <UniformGrid Columns="2">
            <ContentControl Content="{Binding TestViewModelA}" />
            <ContentControl Content="{Binding TestViewModelA}" />
        </UniformGrid>
    </Window>
    

    您的算法属性(StartRange、EndRange 等)和方法(Calculate())存储在 ChildViewModel 中,而 AlgoritmTester 是使 ViewModel 用户友好的 View 。例如,它看起来像这样:
    <UniformGrid Columns="2" Rows="4">
        <TextBlock Text="Minimum:" />
        <TextBox Text="{Binding StartRange}" />
        <TextBlock Text="Maximum:" />
        <TextBox Text="{Binding EndRange}" />
        <TextBlock Text="Elapsed:" />
        <TextBox Text="{Binding Elapsed}" />
        <Button Content="Calculate" Command="{Binding CalculateCommand}" />
    </UniformGrid>
    

    编辑

    关于对上述问题的评论,您的 ParentViewModel 将负责将范围传递给 ChildViewModels。

    例如,如果范围是静态的,您可以在创建时设置它:
    TestViewModelA = new ChildViewModel();
    TestViewModelA.StartRange = 0;
    TestViewModelA.EndRange = 10;
    

    如果它是动态的,我会注册一个 PropertyChanged 事件处理程序来设置范围
    void ParentViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "StartRange":
                TestViewModelA.StartRange = this.StartRange;
                TestViewModelB.StartRange = this.StartRange;
                break;
            case "EndRange":
                TestViewModelA.EndRange = this.EndRange ;
                TestViewModelB.EndRange = this.EndRange ;
                break;
        }
    }
    

    关于mvvm - 如何在一个窗口中创建 VM 之间的 MVVM 父/子关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6681954/

    相关文章:

    mvvm - 是否允许IoC容器使用Messenger/Mediator类?

    arrays - 使用 React 钩子(Hook),我如何更新通过 Prop 传递给 child 的对象?

    hover - 如何在父项悬停时更改伪元素子项?

    javascript - 如何从剑道模板绑定(bind)调用父方法?

    c# - 绑定(bind)复选框命令

    android - 数据绑定(bind) Recyclerview 和 onClick

    mvvm - Kendo UI - 您可以将 View 模型创建为函数吗

    java - Solr 仅在子查询匹配时返回父文档

    oop - 父类和父类(super class)的区别

    java - 从具有相同名称的子类调用方法 - java