假设我正在构建一个汽车导航系统:
- 主窗口将包含屏幕、模式按钮和音量控制。
- 根据系统的模式,屏幕将显示音频,气候或导航面板。
- 在音频模式下,会有另一组模式按钮和一个面板,可以显示收音机,CD或MP3控制。
- MainViewModel应该有一个ScreenViewModel。ScreenViewModel将有一个AudioViewModel, ClimateViewModel和NavigationViewModel。它也将有一个CurrentViewModel属性,它将被设置为音频,气候或导航视图模型,这取决于系统模式。AudioViewModel将类似于ScreenViewModel,保存每个音频系统模式(收音机,CD和MP3)的视图模型,以及用于存储当前模式的视图模型的属性。
将视图绑定到视图模型的XAML应该像这样:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:AudioViewModel}">
<view:AudioPanel />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ClimateViewModel}">
<view:ClimatePanel />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:NavigationViewModel}">
<view:NavigationPanel />
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentViewModel}" />
如果用户正在收听广播,并决定在导航系统中输入目的地,他们将单击导航模式按钮。在MainWindowViewModel上会有一个命令将系统模式更改为"Navigation",并将CurrentViewModel设置为NavigationViewModel。这将导致NavigationView被交换进来。非常干净的溶液。
不幸的是,虽然这样做的事情在执行模式下工作得很好,但当试图在Expression Blend中使用从属视图(例如AudioPanel)时,它会崩溃,因为父视图模型(MainWindowViewModel)不存在以提供AudioViewModel。
似乎在工具包中支持的解决方案,如MVVM Light和Simple MVVM是使用ViewModelLocator代替,然后让视图通过绑定到定位器上的正确属性来设置自己的DataContext。定位器然后提供视图模型的一个实例。
"ViewModelLocator做事的方式"解决了"可设计性"问题,但我不清楚如何表示层次关系和处理一个视图交换到另一个视图。从概念上讲,让视图模型保存子视图模型对我来说更有意义。它正确地表示了视图的层次结构,视图的交换是很容易的,如果不再需要视图,关联的视图模型及其所有从属视图将被垃圾收集,只需删除对父视图的引用。
构建ViewModelLocator来处理分层视图、基于系统模式交换视图和删除视图的最佳实践是什么?
专:- 如何组织视图模型,使层次关系清晰地表示出来?
- 你如何处理交换一个现有的视图为另一个(说取代音频面板与导航面板)?
- 当不再需要关联的父视图时,您如何确保父视图和子视图模型被释放以进行垃圾收集?
似乎视图层次结构中的当前视图是视图'状态'的一部分,因此它将拥有自己的'模型' (viewmodel)实体来管理此关系。我不会使用IoC容器,但我会用它来注册一个工厂,"视图管理器"用来创建"子视图"。
事实证明,在Visual Studio/Blend中有一个XAML设计属性,允许您设置元素的设计时间DataContext
。这只适用于设计期间,所以应该可以继续使用数据模板连接DataContext
(即,ViewModelLocator或ViewManager可能根本不需要)。
例如,假设您有一个名为AudioPanel
的视图和一个名为AudioViewModel
的视图模型。
你只需要在AudioViewModel
中初始化一些设计时数据…
public class AudioViewModel : ViewModelBase
{
public int Volume { get; set; }
public AudioMode Mode { get; set; }
public ViewModelBase ModePanelViewModel { get; set; }
public AudioViewModel()
{
if (IsInDesignMode)
{
Volume = 5;
Mode = AudioMode.Radio;
ModePanelViewModel = new RadioViewModel();
}
}
}
…然后在你的视图中,你只需要声明一个d:DataContext
属性…
<UserControl x:Class="NavSystem.Views.AudioPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:NavSystem.ViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance vm:AudioViewModel, IsDesignTimeCreatable=True}">
只要你为每个在设计时起作用的视图模型编写一个默认构造函数,就应该可以在VS或Blend设计器中查看复合用户界面。
查看这篇博客文章了解更多细节:http://karlshifflett.wordpress.com/2009/10/28/ddesigninstance ddesigndata -在visual studio - 2010 beta2/