MVVM Light ViewModelLocator

6
我将使用MVVM Light在我的应用程序中,解决方案中有多个程序集。如何在不在App.xaml资源中定义的情况下,在每个程序集中都有一个ViewModelLocator呢?
通常ViewModelLocator是这样定义的:
<Application.Resources>
  <ResourceDictionary>
   <ResourceDictionary.MergedDictionaries>
     <ResourceDictionary Source="/Sol1;component/GlobalResources.xaml" />
   </ResourceDictionary.MergedDictionaries>
   <viewModels:ViewModelLocator xmlns:main="clr-namespace:MainModule.ViewModel;assembly=MainModule" x:Key="Locator" d:IsDataSource="True"/> 
</ResourceDictionary>

我在我的解决方案中创建了一个类库的Resources.xaml文件,代码如下:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:ModuleX="clr-namespace:ModuleX">
<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/Common;component/CommonResources.xaml" />
</ResourceDictionary.MergedDictionaries>
<ModuleX:ViewMoldelLocator x:Key="ModuleXLocator" />
</ResourceDictionary>

然而,ModuleXLocator未找到:

<phone:PhoneApplicationPage
       ...
       DataContext="{Binding Main, Source={StaticResource ModuleXLocator}}">
1个回答

7

我不确定我是否回答了你的问题,但我将回答我曾经遇到的类似问题,并提供解决方案。希望这是你想要的。

问题:

你设计了一个很棒的 MVVMLight 应用程序(LittleApp),现在你想将其用作另一个项目(BigApp)中的 UserControl/Page/Window。问题是 LittleApp 在其对应的 App.xaml 中定义了它的 ViewModelLocator,而 BigApp 并没有使用这个文件。结果就是 LittleApp 试图在“BigApp 的 App.xaml”中查找它的 ViewModelLocator,但失败了。

你还想保持 LittleApp 作为一个独立的项目,所以不想乱动这个项目的设计。

可能会出现以下异常:

XMLParseException:在 LittleApp 的根节点中定义的 ViewModelLocator 找不到(因为它是在未使用的 App.xaml 中定义的)

WPFError 40:你使用 MVVMLight 工具创建了两个应用程序,因此两个应用程序都有一个名为“Locator”的 App.xaml,指向一个名为“Main”的 ViewModel。LittleApp 将能够解析此问题,但它将指向错误的 MainViewModel,从而导致属性冲突。

解决方案:

你需要将 LittleApp 的 ViewModelLocator 定义为 LittleApp 的 MainView 根节点中的资源。因此,如果你的 MainView 是一个名为 LittleApp/View/MainView.xaml 的 UserControl,那么它应该如下所示。

<UserControl x:Class="LittleApp.View.MainView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:vm="clr-namespace:LittleApp.ViewModel"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">

    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="../Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
            <vm:ViewModelLocator x:Key="Locator"/>
        </ResourceDictionary>
    </UserControl.Resources>

    <UserControl.DataContext>
        <Binding Source="{StaticResource Locator}"/>
    </UserControl.DataContext>

    <Grid>
       .
       .  Your Control Design
       .
       .
       .
    </Grid>
</UserControl>

现在,BigApp只需要引用LittleApps程序集并将其添加为您想要添加的控件根节点中的命名空间。就像这样。
<Page x:Class="BigApp.View.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"      
      xmlns:LittleView="clr-namespace:LittleApp.View;assembly=LittleApp"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300">

    <Grid>
        <LittleView:MainView/>
    </Grid>
</Page>

谢谢你的回答!你肯定带我走上了正确的道路。实际上,我在 LittleAppResources.xaml 中添加了 <vm:ViewModelLocator x:Key="Locator"/>,然后在每个视图中通过 ResourceDictionary 添加了对该 xaml 的引用,就像你在 UserControl 中所做的一样。 :) - rdon
这个回答直接解决了我的问题,谢谢!不过我有一个问题:这会导致 LittleApp 的 VML 覆盖 BigApp 的吗?我该如何在 VML 之间共享数据?(例如,我不希望每个 LittleApp 窗口都必须重新加载父级已经从磁盘加载的数据,但我也不希望多个 LittleApps 共享状态。) - metao
1
@metao,你不应该在VMLocators之间共享信息。每个项目应该有一个VML,它将视图链接到它们各自的视图模型。如果它们具有父子关系,则父视图模型应创建并包含每个子视图模型,您可以直接在它们之间传递信息或使用信使服务。然后,在xaml中,BigApp的数据上下文将是(Locator.)Main(如在app.xaml中设置),您可以将LittleApp的数据上下文设置为代码或BigApp的xaml中的(Locator.)Main.LittleAppViewModel的绑定。 - Josh C
1
@metao 所有LittleApp的元素都将继承这个新的数据上下文,并通过其自身在项目中定义的绑定正确地填充视图。值得注意的是,如果您在使用mvvmLight,则可以使用SimpleIoC在BigApps VML中注册LittleApp的MainViewModel - 例如 SimpleIoc.Default.Register<LittleApp.ViewModel.MainViewModel>();。然后,您可以在BigApp的MainViewModel中调用现有/新实例(例如在构造函数中)LittleAppViewModel = ServiceLocator.Current.GetInstance<LittleApp.ViewModel.MainViewModel> ()。这就是您要绑定为数据上下文的内容。 - Josh C
@Josh C,感谢您在我努力改进MVVM实现的过程中提供了非常有用的答案。然而,我在使用XAML时遇到了ViewModelLocator的概念难以理解的问题。当我使用您的方法时,数据绑定失败,因为它在“Locator”对象上查找属性,而不是ViewModel。但是,如果我使用SimpleIoc ServiceLocator在代码后台分配DataContext,一切都可以正常工作。您有什么建议可以让我从XAML中使其正常工作吗? - Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接