1

我有多个视图模型实例:

<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 使用视图的参数化构造函数,但这不受支持。另一个想法是为视图使用不同的类,但这很快就会使代码膨胀!

4

1 回答 1

2

快速回答和总结:

正如您所注意到的,您真正想要的是从 XAML 参数化您的 ViewModel。所以我的直觉是编写一个附加行为来提供为您的 ViewModel 传递参数的能力。更具体地说,想到的是我们想要一个单独的附加行为类,我们可以指定(a)我们想要的 ViewModel b)那个 ViewModel 的参数。为了在单个类中同时满足这两个需求,同时尽可能保持DRY,我认为使用“混合”行为是最简单的,因为混合行为不是静态类,因此使用它们似乎更容易将两条相关信息一起传递。

解释:

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

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

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

现在让我们根据您的情况来说明,如果您只使用常规的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(并公开一个“类型”附加属性,如上所示)。另一个缺点是似乎更难找到一种方法来添加额外的参数来传递。

与上述方法稍有替代但在DRY方面同样不足的方法是拥有一个包含多个名称如“CreateViewModel_1_WithType”的附加属性的类(称为“MyViewModelCreator” - 这次没有“1”) "、"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" />

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

现在让我们考虑如果我们使用“混合”行为它会如何工作:

您将编写一个派生自类型化 Behavior 的类 - 对于您的视图,它可能是Behavior<UserControl>,因此您的类标题可能如下所示
public class ViewModelSetupBehavior : Behavior<UserControl>:在此类中,您将:(1)注册任意数量的依赖属性,包括“类型”依赖属性和“ViewModelName”依赖属性,以及(2)您将覆盖该OnAttached()方法,您将在其中实例化任何 ViewModel由“ViewModelName”依赖属性的值指示,并且还将“类型”依赖属性的值传递给它。同样,这也是处理任何其他必需品的地方,例如为视图设置 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>

现在请注意,我们可以使用单个Behavior 类来创建所有ViewModel,并且我们可以传入几个参数来指示我们希望如何实例化 ViewModel。

于 2013-01-24T16:53:20.820 回答