ViewModelLocator MVVM Light中的ViewModels

时间:2013-01-02 22:34:17

标签: xaml mvvm-light

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

2 个答案:

答案 0 :(得分:124)

首先,让我们看一下ViewModelLocator的作用以及我们使用它的原因:

ViewModelLocator在App.xaml页面上被声明为一个对象,是一个应用程序单例。我们将会运行一个,只有一个可用于应用程序。

ViewModelLocator是MVVM Light中所有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),这意味着您可以创建设计时服务来提供您的视图模型数据,以便您在视觉中查看Studio包含实际数据。

答案 1 :(得分:1)

MVVM Light有许多不错的功能,但在我看来,服务定位器会在视图模型上创建不必要的视图依赖关系。理想情况下,我希望在库A中有ViewModelLocator,在库B中有视图模型,在库C中有视图。然后我可以根据需要混合和匹配这些用于未来的项目。但是,在MVVM Light的设计中,据我所知,视图(库C)将始终依赖于ViewModelLocator(这没关系)但是因为ViewModelLocator(库A)将始终依赖于查看模型(库B),然后视图将始终依赖于视图模型(这是不可行的,因为视图现在必须包括它在所有产品中使用的所有视图模型库)。

我相信Prism通过某种方式使用字符串键解决了这个问题。我错过了什么吗?

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

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