前阵子有人问我MVVM模式下,在View中嵌套View,切换View。想一想还是写下来吧。
主要就是用到 ContentControl 和 DataTemplate,这算是一种 ViewModel First 的思想吧。
其实好多MVVM的框架,也都提供这样的功能。在ContentControl绑定ViewModel,就可以显示 对应的View。比如 Caliburn.Micro(CM框架).
MVVMLight应该是没有提供的,对他我本身不是很熟,以前知道他很轻,看过原码,真心没有多少,最近在RT中用了下,发现它也加入了IOC的东东。
以下代码没用使用任何第三方框架。
using System.Windows.Input; namespace ContentDemo { // ViewModelBase 只是实现了 INotifyPropertyChanged class MainViewModel : ViewModelBase {
// 如果有IOC这块的东西 可以用IOC代替。 private readonly FirstViewModel _firstViewModel = new FirstViewModel(); private readonly SecondViewModel _secondViewModel = new SecondViewModel(); private object _viewModel; /// <summary> /// 要绑定和切换的ViewModel /// </summary> public object ViewModel { get { return _viewModel; } set { if (_viewModel == value) { return; } _viewModel = value; OnPropertyChanged(); } } // 下面两个 Command只是为了 切换 ViewModel用的。 private ICommand _firstCommand; public ICommand FirstCommand { get { return _firstCommand = _firstCommand ?? new DelegateCommand(obj => { ViewModel = _firstViewModel; }); } } private ICommand _secondCommand; public ICommand SecondCommand { get { return _secondCommand = _secondCommand ?? new DelegateCommand(obj => { ViewModel = _secondViewModel; }); } } } }
这个是ViewModel的代码,ViewModel OK啦,我们就来写View了。
<Window x:Class="ContentDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ContentDemo" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <local:MainViewModel x:Key="MainViewModel" /> <DataTemplate DataType="{x:Type local:FirstViewModel}"> <local:FirstView /> </DataTemplate> <DataTemplate DataType="{x:Type local:SecondViewModel}"> <local:SecondView /> </DataTemplate> </Window.Resources> <Grid DataContext="{StaticResource MainViewModel}"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <ContentControl Grid.ColumnSpan="2" Content="{Binding Path=ViewModel}" /> <Button Grid.Row="1" Grid.Column="0" Content="ViewModel 1" Command="{Binding Path=FirstCommand}"/> <Button Grid.Row="1" Grid.Column="1" Content="ViewModel 2" Command="{Binding Path=SecondCommand}"/> </Grid> </Window>
其实这也没什么,关键的地方就是,ContentControl的Content要绑定ViewModel,这是用来显示和切换ViewModel对应的View的。
在资源中加入DataTempalte,DataType设置成ViewModel的类型,DataTemplate 写上你的View,只样就可以自动匹配的你ViewModel啦。
注:在WinRT中的DataTemplate是没有DataType属性的,SL里有没有,我记不住了。可以用我最下面说的办法来弄。
下在是 二个ViewModel和View的代码,都很简答,就是为了展示一下。
class FirstViewModel { public string Content { get; set; } public FirstViewModel() { Content = "第一个ViewModel"; } } class SecondViewModel { public string Content { get; set; } public SecondViewModel() { Content = "第二个ViewModel"; } }
<UserControl x:Class="ContentDemo.FirstView" 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"> <Grid> <Border Background="DarkGray"> <TextBlock Text="{Binding Path=Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </Grid> </UserControl>
<UserControl x:Class="ContentDemo.SecondView" 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"> <Grid> <Border Background="DarkMagenta"> <TextBlock Text="{Binding Path=Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </Grid> </UserControl>
这样,我们切换MailViewModel中的ViewModel的时候,就会换成不同的UI了,DataTemplate 中内容的DataContext 也会是相应的ViewModel。
写的很不好,我的语文虽然不是体育老师交的,也是数学老教交出来的。
还是直接源码吧:Code
源码是2013写的,打不开的自行修改 项目文件或复制源码到新项目吧。
可能现在有人发现了,这样每一个用到的ViewModel和都至少写上一个DataTemplate,这样很不爽,重复的代码太多啦。
下一步的关键就是 DataTemplateSelector。说到这个,大家可能就会了,下篇再分解吧。嘿嘿