将我所有的 ViewModel 存储在 SimpleIoc 中是否正确?例如,我有三个页面 MainPage、Photos、Directories(因此三个 ViewModels -> MainVM、PhotosVM、DirectoriesVM)。我应该将每个页面中的 DataContext 设置为 ViewModelLocator 中的 View Model Property 还是将 ViewModels 作为属性嵌套在 MainVM 中并将每个页面 DataContext 绑定到 Main.PhotosVMProperty、Main.DirectoriesVMProperty 等等?谁能解释一下 IoC 的想法和目的?
2 回答
首先,让我们看看 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 构造函数。
我为什么要做所有这些工作?
- 使您的课程易于单元测试
- 使您的代码接口驱动。这意味着您引用的是接口而不是具体的类
- 使您的代码松散耦合。这意味着某人可以更改接口的实现,而使用该接口的类不需要重新编码。
- 以自动方式解决您的类依赖关系。
- 在 MVVM Light 中,您会看到它可以判断何时在设计模式 (
ViewModelBase.IsInDesignModeStatic
) 下运行,这意味着您可以创建设计时服务来提供您的视图模型数据,以便您在 Visual Studio 中的视图包含实际数据。
MVVM Light 有很多不错的功能,但在我看来,Service Locator 创建了视图对视图模型的不必要依赖。理想情况下,我希望在库 A 中拥有 ViewModelLocator,在库 B 中拥有视图模型,在库 C 中拥有视图。然后我可以根据未来项目的需要混合和匹配这些。但是,在 MVVM Light 的设计中,据我所知,视图(库 C)将始终依赖于 ViewModelLocator(这没关系),但是因为 ViewModelLocator(库 A)将始终依赖于视图模型(库 B),那么视图将始终依赖于视图模型(这是不行的,因为视图现在必须包含它曾经在所有产品中使用过的所有视图模型库)。
我相信 Prism 以某种方式使用字符串键来解决这个问题。我错过了什么吗?
哎呀!我想我只是回答了我自己的问题。解决方案是使库 A(ServiceLocator)特定于特定的解决方案(产品)。然后它只包含对该解决方案的视图模型的引用。然后视图依赖于这个 ServiceLocator,而后者又依赖于该产品的所有视图模型。最终结果是视图仅取决于将用于该产品的视图模型。我们为每个解决方案复制 ServiceLocator 这一事实没有问题,因为该模块仅包含特定于解决方案的代码。ServiceLocator 的组件(例如 SimpleIoc 类)当然对所有解决方案都是通用的,但这些组件已被分解为我们在 ServiceLocator 中调用的可重用类。
总结一下,我要解决的问题是假设一个解决方案有 6 个视图模型,其中四个密切相关,其中两个密切相关。因此,我们创建了两个程序集,每个程序集都包含密切相关的视图模型。假设我们设计了一个使用一组视图模型的产品,并且该解决方案设计为运行 Windows 8。现在视图都不同了,我们只想重用一组(组合)视图模型。因此,我们只需创建一个新的 ServiceLocator 程序集,它指向该视图模型程序集以及我们需要的任何其他程序集。我们的新 Windows 8 视图现在依赖于这个新的 ServiceLocator 程序集,并且仅依赖于我们的新产品(解决方案)中使用的视图模型。