MVVMLight ViewModelLocator在UserControl上的应用

4

能否在UserControl上使用MVVMLight的ViewModelLocator?我已经按照MainWindow上的方式将其添加到我的UserControl中,但是在VS2010中出现了一个错误/弹出窗口,指出“找不到名为'Locator'的资源。资源名称区分大小写。”

有人尝试过这个吗?

到目前为止,我所拥有的代码基本上是标准的MVVMLight WPF起始应用程序...

UserControl

<UserControl x:Class="NavTest3.PersonControl"
         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:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         Height="116" MinWidth="250" Width="300"
         DataContext="{Binding Person, Source={StaticResource Locator}}"
         >

<!---->
<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skins/MainSkin.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

App.xaml 包含...

<Application.Resources>
    <!--Global View Model Locator-->
    <vm:ViewModelLocator x:Key="Locator"
                         d:IsDataSource="True" />
</Application.Resources>

问题出在将 "DataContext="{Binding Person, Source={StaticResource Locator}}" 设置到用户控件上。"

如上所述,这样做意味着每个此用户控件实例都将共享相同的 ViewModel,但我想先理解这个问题再进行下一步操作。


发布您的代码会很有帮助。 - Mike Post
你可以发布与这个特定部分相关的定位器代码吗? - ecathell
3个回答

4

是的,你可以。你需要在用户控件中创建一个静态资源。

<UserControl x:Class="MvvmLight1.UserControl1"
             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:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"

             xmlns:vm="clr-namespace:MvvmLight1.ViewModel"
             >

    <UserControl.Resources>
        <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
    </UserControl.Resources>
    <Grid>

    </Grid>
</UserControl>

但是我认为在用户控件中使用MVVM Light ViewModelLocator不是一个好主意,因为它是一个静态属性。当你要实例化多个用户控件时,它们都会有相同的公共ViewModel,因此它们会表现得一样,这不是我们希望在用户控件中看到的情况,除非你只想在整个项目中使用一次。

为了解决这个问题,你需要修改ViewModelLocator,将所有属性设置为非静态,例如:

 public class ViewModelLocator
    {
        //         v--- You got to comment this out
        private /*static*/ MainViewModel _main;

        public ViewModelLocator()
        {            
            CreateMain();
        }

        public /*static*/ MainViewModel MainStatic
        {
            get
            {
                if (_main == null)
                {
                    CreateMain();
                }

                return _main;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public MainViewModel Main
        {
            get
            {
                return MainStatic;
            }
        }

        public /*static*/ void ClearMain()
        {
            _main.Cleanup();
            _main = null;
        }

        public /*static*/ void CreateMain()
        {
            if (_main == null)
            {
                _main = new MainViewModel();
            }
        }

        public /*static*/ void Cleanup()
        {
            ClearMain();
        }
    }

我尝试过那样做,但效果相同。虽然您的示例中省略了UserControl中的dataContext,但是绑定如何工作呢? - S Rosam
一个应用程序的架构取决于它要解决的问题,因此,在不知道问题的情况下,你所做的说法是苛刻的。此外,依我之见,你的解决方案存在缺陷,因为定位器是静态的,它通过其属性返回的视图模型(这就是你绑定到的内容)不必是静态或单例的,甚至可以依赖于应用程序状态。有相当多的自由度... 另外,据我所知,资源名称不能声明两次,并且必须在app.xaml中定义,以便其他视图也可以使用它。 - AxelEckenberger
抱歉!!我忘记粘贴数据上下文了。但它的使用方式与MVVM Light中的相同。 - Ehsan Zargar Ershadi
好的。正如你所说,架构非常重要。由于这种情况是为了测试导航而设计的,我可能已经搞砸了一些东西。里面肯定存在循环引用/依赖关系。我会从头开始重新思考。谢谢。 - S Rosam

2
可能是资源加载顺序的问题...尝试将元素的DataContext分配给层次结构较低的元素,例如在用户控件下面的网格中。
<UserControl x:Class="NavTest3.PersonControl" 
    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:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d" Height="116" MinWidth="250" Width="300">

    <UserControl.Resources> 
        <ResourceDictionary> 
            <ResourceDictionary.MergedDictionaries> 
                <ResourceDictionary Source="Skins/MainSkin.xaml" /> 
            </ResourceDictionary.MergedDictionaries> 
        </ResourceDictionary> 
    </UserControl.Resources>

    <Grid DataContext="{Binding Person, Source={StaticResource Locator}}">
        <!-- content -->
    </Grid>
</UserControl>

编辑:

尝试使用Visual Studio属性部分中的数据绑定来设置绑定,并确认您是否看到了Locator。选择应放置您的DataContext的元素,找到DataContext属性,然后单击到属性值区域。现在应该会打开一个对话框,您可以在其中选择定位器。可能这可以解决问题,或者帮助您找到解决方案。在绑定之前还要重新构建项目。


嗨,我尝试了这个,它抛出了相同的错误(找不到名为“Locator”的资源),所以我仍然认为它可能无法看到ViewModelLocator(或无法创建它)。 - S Rosam

0

确认您的ViewModels中没有抛出任何异常。通常当我遇到这个错误时,无法实例化Locator,因为在上游ViewModel构建之一中抛出了异常。您能贴出您的Locator构造函数吗?

如果您想自行解决此问题,请在VMLocator中的第一个CreateVM语句上设置断点,并查看哪个VM引发了异常。


嗨,谢谢你的提示。我按照你的指示操作了,可以确认视图模型已经被成功创建。我相信问题在于视图(UserControl)无法“看到”视图模型定位器。我只是不知道为什么会这样。 - S Rosam
你的ViewModelLocator在哪里?我一直在用户控件中使用它,而且它运行良好。我还将我的视图和视图模型放在不同的程序集中,因此我有另一层间接性。问题肯定不是下面所建议的那样简单。只要你的依赖关系正确,而且你的VM没有抛出错误阻止定位器实例化,ViewModelLocator就可以很好地工作。 - ecathell

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