ViewModelLocator MVVM Light中的ViewModels

34

在SimpleIoc中存储所有的ViewModel是否正确?例如,我有三个页面MainPage、Photos、Directories(因此有三个ViewModel-> MainVM、PhotosVM、DirectoriesVM)。应该在每个页面中将DataContext设置为ViewModelLocator中的ViewModel属性,还是将ViewModel嵌套为MainVM的属性,并将每个页面的DataContext绑定到Main.PhotosVMProperty、Main.DirectoriesVMProperty等属性?有人可以解释一下IoC的思想和目的吗?


1
希望这个会对您有所帮助:https://dev59.com/PGYr5IYBdhLWcg3wR4Ys - User1551892
你能提供一些与你的第二种方法相关的代码吗?你如何在MainViewModel中定义PhotosVMProperty...你的问题非常有趣... - User1551892
2个回答

133

首先,让我们看一下ViewModelLocator的作用以及为什么我们使用它:

ViewModelLocator在我们的App.xaml页面中声明为对象,并且是应用程序单例。当应用运行时,我们将拥有一个且仅有一个该对象可用。

在MVVM Light中,ViewModelLocator是所有ViewModel的源。对于每个ViewModel,我们都会在ViewModelLocator上拥有一个属性,允许我们为View获取ViewModel。这段代码看起来像这样:

public class ViewModelLocator
{
    public MainPageViewModel MainPage
    {
        get { return new MainPageViewModel(); }
    }
}

这是我的 App.xaml 文件的一部分:

<Application.Resources>
    <vm:ViewModelLocator
        x:Key="ViewModelLocator" />
</Application.Resources>

这是来自View.xaml的一段内容。

DataContext="{Binding MainPage, Source={StaticResource ViewModelLocator}}"

目前为止一切顺利。回答你的第一个问题,MVVM Light中是否必须使用IoC?不是必须的。因为ViewModelLocator会将完全构建并实例化的视图模型提供给视图。

现在来到你的第二个问题:IoC的目的是什么?

IoC的设计旨在让你能够实现以下内容:

在Mvvm Light中,你可以这样做:

public class ViewModelLocator
{
    public ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        if (ViewModelBase.IsInDesignModeStatic)
        {
            SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
        }
        else
        {
            SimpleIoc.Default.Register<IDataService, DataService>();         
        }

        SimpleIoc.Default.Register<MainViewModel>();
    }

    public MainViewModel Main
    {
        get { return SimpleIoc.Default.GetInstance<MainViewModel>(); }
    }
}

public class MainViewModel
{
    public ObservableCollection<Foo> Foos { get; set; }

    public MainViewModel(IDataService dataService)
    {
        _dataService=dataService;
        Foos=_dataService.GetFoos();
    }
}

当我调用MainViewModel解析时

SimpleIoc.Default.GetInstance<MainViewModel>()

内部发生的是 SimpleIoc 检查 MainViewModel 是否有任何依赖项(构造函数中的参数)。然后它尝试通过查看已经在其注册的接口来解决这些参数。它以递归方式执行此操作,因此如果 DataService 有一个依赖项,则在实例化时将被实例化并传递给 DataService 构造函数。

为什么要做所有这些工作呢?

  1. 使您的类易于单元测试
  2. 使您的代码面向接口编程。这意味着您引用接口而不是具体类。
  3. 使您的代码松散耦合。这意味着某人可以更改接口的实现,消费该接口的类不关心并且不需要重新编码。
  4. 以自动化方式解决类的依赖关系。
  5. 在 MVVM Light 中,您会看到它可以告诉它是否在设计模式下运行(ViewModelBase.IsInDesignModeStatic),这意味着您可以创建设计时服务来提供视图模型数据,以便您的 Visual Studio 中的 View 包含实际数据。

@Faster Solutions,它是否可混合?这意味着在视图模型中没有无参数构造函数,Blend 将无法获取用于数据绑定的视图模型,对吗? - Mussammil

1
MVVM Light有许多不错的特性,但在我看来,服务定位器会在视图与视图模型之间创建不必要的依赖。理想情况下,我希望将ViewModelLocator放在Library A中,将视图模型放在Library B中,将视图放在Library C中。这样,我可以根据需要进行混合和匹配以供未来项目使用。然而,在MVVM Light的设计中,据我所见,视图(Library C)将始终依赖于ViewModelLocator(这是可以接受的),但由于ViewModelLocator(Library A)将始终依赖于视图模型(Library B),因此视图将始终依赖于视图模型(这是不可接受的,因为视图现在必须包含所有曾经在所有产品上使用过的视图模型库)。
我认为Prism通过某种方式使用字符串键来解决了这个问题。我是否遗漏了什么?

哎呀!我想我刚刚回答了自己的问题。解决方案是将Library A,即ServiceLocator,针对特定解决方案(产品)进行设置。它包含仅适用于该解决方案的视图模型的引用。然后,视图依赖于此ServiceLocator,而此ServiceLocator又依赖于该产品的所有视图模型。最终结果是,视图仅依赖于在该产品中将要使用的视图模型。我们为每个解决方案复制ServiceLocator并没有问题,因为这个模块只包含特定于该解决方案的代码。当然,ServiceLocator的组件(例如SimpleIoc类)对于所有解决方案都是通用的,但这些已被拆分为可重用类,并在ServiceLocator中调用。

简而言之,我试图解决的问题是:假设一个解决方案有6个视图模型,其中4个密切相关,另外2个也密切相关。因此,我们创建了两个程序集,每个程序集包含密切相关的视图模型。假设我们设计了一个使用一组视图模型的产品,并且该解决方案旨在运行Windows 8。现在,这些视图都不同,我们只想重用一个(程序集)视图模型集。所以我们只需创建一个新的ServiceLocator程序集,指向这个视图模型程序集和任何其他我们需要的程序集。我们的新Windows 8视图现在依赖于这个新的ServiceLocator程序集,以及我们新产品(解决方案)中使用的视图模型。

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