【问题标题】:How to tag viewmodel when using ViewModelLocator?使用 ViewModelLocator 时如何标记视图模型?
【发布时间】:2013-01-22 09:35:50
【问题描述】:

我有多个视图模型实例:

<views:MyView x:Name="view1" mefed:ViewModelLocator.NonSharedViewModel="MyViewModel" />
<views:MyView x:Name="view2" mefed:ViewModelLocator.NonSharedViewModel="MyViewModel" />

这两个实例应该监听不同的消息。因此,我需要以某种方式标记这些视图模型实例。怎么样?

我正在使用MEFedMVVMPrism。应该有一种方法可以让视图模型了解某些状态。例如:

<views:MyView x:Name="view1" mefed:ViewModelLocator.NonSharedViewModel="MyViewModel">
  <!-- Let the viewmodel know it is of type X -->
</views:MyView>
<views:MyView x:Name="view2" mefed:ViewModelLocator.NonSharedViewModel="MyViewModel">
  <!-- Let the viewmodel know it is of type Y -->
</views:MyView>

如何实现?

在理想情况下,您可以通过 XAML 使用视图的参数化构造函数,但这不受支持。另一个想法是为视图使用不同的类,但这很快就会使代码膨胀!

【问题讨论】:

    标签: wpf mvvm viewmodel


    【解决方案1】:

    快速回答和总结:

    如您所见,您真正想要的是从 XAML 参数化您的 ViewModel。所以我的直觉是写一个附加行为来为你的视图模型提供传递参数的能力。更具体地说,想到的是我们想要一个 single 附加的 Behavior 类,我们可以指定 both (a) 我们想要哪个 ViewModel (b) 该 ViewModel 的参数。为了在单个类中满足两个这两个愿望,同时尽可能地成为DRY,我认为使用“混合”行为是最简单的,因为混合行为不是静态类,因此它似乎更容易使用它们将两条相关信息一起传递。

    解释:

    首先快速免责声明:我没有使用过 MEFedMVVM 或 Prism(但我使用过其他 MVVM 库),所以我的回答使用了我最近学会使用的更通用的方法。因此,当您以“正常”方式使用事物(即自动连接 DataContext 等)时,这种方法不依赖 Prism 可能为您提供的任何“神奇”东西,所以让这种心态成为框架。

    关于“常规”附加行为和“混合”行为之间差异的好文章,我喜欢this blog post。我不会在这里重复他的解释,但我注意到的关键是常规的附加行为似乎只依赖于单个条信息(即单个“参数”)来执行它的东西。如(来自博文):

    <GridView local:ItemClickNavBehavior.Destination="Home" ...>
    

    现在让我们根据您的情况来说明,如果您只使用 regular Attached Bahaviors,它会如何工作。您将编写一个附加行为类,将其称为“MyViewModel1Creator”,它会: (1) 注册一个名为“Type”的附加属性,并且 (2) 包括一个“类型”的更改回调处理程序(在初始设置时也会调用它 - 请参阅链接博客文章中的“HookupBehavior”方法)。在此更改回调中,您将实例化“ViewModel1”并将“Type”附加属性的值传递给它。同样在此方法中,您可以处理任何其他必需品,例如为视图设置 DataContext 等。您可以使用第一个参数访问附加属性附加到的对象(在本例中为视图对象)回调处理程序(依赖对象参数)。

    那么您对“MyViewModel1Creator”类的 Xaml 使用将如下所示:

    <views:MyView x:Name="view1" MyBehaviors:MyViewModel1Creator.Type="X" />
    <views:MyView x:Name="view2" MyBehaviors:MyViewModel1Creator.Type="Y" />
    

    虽然这可行,但我发现这种方法有一个缺点(使用常规附加属性)。要使用这种方法,您必须为每个 ViewModel 创建一个单独的 Attached Behavior 类,这意味着如果您有 3 个 ViewModel(“ViewModel1”、“ViewModel2”、“ViewModel3”),那么您需要编写 3 个 Attached Behavior 类( “ViewModel1Creator”、“ViewModel2Creator”、“ViewModel3Creator”)。每个都将实例化其各自的 ViewModel(并公开一个“类型”附加属性,如上所示)。另一个缺点是似乎更难找到添加additional参数的方法。

    与上述方法稍有不同,但 同样不足 就成为 DRY 而言,将是拥有一个类(称之为“MyViewModelCreator” - 没有“1”这个time) 包含几个附加属性,名称如“CreateViewModel_1_WithType”、“CreateViewModel_2_WithType”、“CreateViewModel_3_WithType”等。其用法如下所示:

    <views:MyView x:Name="view1"
                  MyBehaviors:MyViewModelCreator.CreateViewModel_1_WithType="X" />
    <views:MyView x:Name="view2" 
                  MyBehaviors:MyViewModelCreator.CreateViewModel_1_WithType="Y" />
    

    同样,这些方法不是很干,所以我们真的需要......

    现在让我们考虑一下如果我们使用 "Blend" behavior 会如何工作

    您将编写一个派生自类型化 Behavior 的类 - 对于您的视图,它可能是 Behavior&lt;UserControl&gt;,因此您的类标题可能类似于:
    public class ViewModelSetupBehavior : Behavior&lt;UserControl&gt;。在本课程中,您将:(1) 注册任意数量的依赖属性,包括“类型”依赖属性和“ViewModelName”依赖属性,以及 (2) 您将覆盖OnAttached() 方法,您将在该方法中实例化由“ViewModelName”依赖属性的值指示的任何ViewModel,并将“Type”依赖属性的值传递给它。同样,这也是处理任何其他必需品的地方,例如为视图设置 DataContext 等。您可以使用 AssociatedObject 访问行为“附加到”的对象(在本例中为您的视图)属性。

    这可以让你这样做:

    <views:MyView x:Name="view1">
        <i:Interaction.Behaviors>
            <MyBehaviors:ViewModelSetupBehavior ViewModelName="ViewModel1" Type="X" SomeOtherParam="bla" />
        </i:Interaction.Behaviors>
    </views:MyView>
    
    <views:MyView x:Name="view2">
        <i:Interaction.Behaviors>
            <MyBehaviors:ViewModelSetupBehavior ViewModelName="ViewModel1" Type="Y" />
        </i:Interaction.Behaviors>
    </views:MyView>
    

    现在请注意,我们可以使用 single Behavior 类来创建 all 我们的 ViewModel,我们可以传入 several em> 参数来指示我们希望如何实例化 ViewModel。

    【讨论】:

    • 优秀的答案!您指出了我迄今为止遇到的确切问题,并提出了一个可能有效的有趣解决方案。我会试试看!还有一件事;如果view2 有一个view3 有另一个视图模型怎么办?在这种情况下,我如何将父 (view2) 视图模型的行为传播到子 (view2) 视图模型?
    • 很高兴听到我的回答很有用。为了解决您的后续问题,我想我需要澄清一下。让我看看我是否可以重申一下情况:view2 的类型是 MyView,而 view2 的 ViewModel 的类型是 ViewModel1(如我们上面的代码所示)。 view3 是什么类型? view3 的 ViewModel 的类型是什么?最后,您究竟想从 view2 的 ViewModel 传播到 view3 的 ViewModel 什么?
    • 考虑view2 的交互行为。这意味着 Y 类型为 view2 的视图模型。如果我希望它的子视图view3 继承此行为,以便获得相同类型的视图模型怎么办?也许我需要一个继承的DependencyProperty 来建立这样的关系?
    • @l33t 这是一个非常有趣的问题!事实上,它可能需要一个新的 SO 问题,以便给予它适当的关注和来自整个社区的输入。从本质上讲,您似乎在寻求一种优雅的方式,在您的特定 Blend 行为中利用某种基于视觉树(或可能基于逻辑树)的属性继承。
    • 第一种探索方法是内置 WPF 属性值继承能力。对于这种方法,您需要将“类型”DP 注册为附加 DP,并在注册附加 DP 时设置正确的继承标志。但是,此路线可能存在问题,可能会阻止其工作。例如,我从 MSDN 文档中看到一些参考,其中提到持有要继承的值的父级必须是“某个逻辑树”的一部分,如果我的 WPF Inspector 应用程序告诉我的是真的,那么它似乎 Blend 行为可能不在逻辑树中。
    猜你喜欢
    • 2012-07-15
    • 2016-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    • 1970-01-01
    • 2020-05-08
    相关资源
    最近更新 更多