如何在使用ViewModelLocator时标记viewmodel?

时间:2013-01-22 09:35:50

标签: wpf mvvm viewmodel

我有一个viewmodel的多个实例:

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

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

我正在使用MEFedMVVMPrism。应该有一种方法让视图模型知道一些状态。 E.g:

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

1 个答案:

答案 0 :(得分:2)

快速解答和摘要:

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

说明:

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

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

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

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

然后你的Xaml使用“MyViewModel1Creator”类看起来像:

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

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

对于上述方法略有不同的方法,但同样不足DRY而言,就是拥有一个类(称之为“MyViewModelCreator” - 没有“1” “这一次”包含几个附加属性,其名称如“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<UserControl>,因此您的类标题可能如下所示:
public class ViewModelSetupBehavior : Behavior<UserControl>。在这个类中,你会:(1)注册任意数量的依赖属性,包括“Type”依赖属性,“ViewModelName”依赖属性,(2)您将覆盖OnAttached()方法,您将在其中实例化由“ViewModelName”依赖项属性的值指示的ViewModel,并向其传递“类型”依赖项属性的值。同样,这也是处理任何其他必需品的地方,例如为View等设置DataContext。您可以使用{{1访问行为“附加到”的对象(在本例中为View)属性。

这可以让你这样做:

AssociatedObject

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

相关问题