赞
踩
在WPF中有一种控件叫做ContenControl,其有一个object类型的依赖属性Content。我们常规做法是将UserControl赋值给Content,但是这么做两个问题。
1)一般我们会将Content绑定到ViewModel层的数据,如果该数据为UserControl,那么在ViewModel层中就出现了View层。
2)一般使用Content绑定UserControl的时候,都是用与动态切换的场景,如果我们有比较复杂的UserControl,那么在每一次切换界面的时候都会进行界面的销毁和重新渲染,影响性能。
上述两个问题有解决办法吗?如果我们将ViewModel绑定到Content就可以解决这个问题。
那么问题又出现了,如果我们将Content绑定ViewModel,该如何与ViewModel对应的View进行关联呢?
其实在WPF中有一个叫资源的东西,如果我们将数据模板存放在资源中,并且给数据模板指定控件模板,那么我们将Content设置为一个数据模板的时候,其实这个数据模板已经在资源中提前绑定到控件模板上了,所以在界面显示的就是我们的控件模板。
直接上代码:
<Window x:Class="WpfApp1.MainWindow" 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:local="clr-namespace:WpfApp1" xmlns:viewModel="clr-namespace:WpfApp1.ViewModel" mc:Ignorable="d" x:Name="mainWindow" Title="MainWindow" Height="450" Width="800"> <Window.DataContext> <viewModel:MainViewModel Window="{x:Reference mainWindow}"/> </Window.DataContext> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <ComboBox Grid.Column="0" VerticalAlignment="Top" Margin="10" SelectedIndex="{Binding SelectedIndex}"> <ComboBoxItem Content="Page1" IsSelected="True"/> <ComboBoxItem Content="Page2"/> <ComboBoxItem Content="Page3"/> </ComboBox> <ContentControl Grid.Column="1" Content="{Binding ViewModelContent}"/> </Grid> </Window>
public class MainViewModel : BaseModel { public ResourceDictionary Resources { get; set; } = new ResourceDictionary(); private int selectedIndex; public int SelectedIndex { get { return selectedIndex; } set { selectedIndex = value; OnPropertyChanged(); SetViewModelIndex(value); } } private object viewModelContent; public object ViewModelContent { get{ return viewModelContent; } set { viewModelContent = value; OnPropertyChanged(); } } private FrameworkElement window; public FrameworkElement Window { get { return window; } set { window = value; window.Resources = this.Resources; } } private void SetViewModelIndex(int index) { if (index >= 0 && index < this.models.Count) ViewModelContent = this.models[index]; } public MainViewModel() { this.SetView(new ViewModel1(), typeof(UserControl1)); this.SetView(new ViewModel2(), typeof(UserControl2)); this.SetView(new ViewModel3(), typeof(UserControl3)); SetViewModelIndex(0); } private List<object> models = new List<object>(); private void SetView(object viewModel, Type type) { DataTemplateKey key = new DataTemplateKey(viewModel.GetType()); DataTemplate template = new DataTemplate(viewModel.GetType()); template.VisualTree = new FrameworkElementFactory(type); this.Resources.Add(key, template); this.models.Add(viewModel); } }
上面SetView方法和下面的效果是一致的,,,并且下面的写法感觉更方便。。。
<Window x:Class="MVVMLight.MainWindow" 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:local="clr-namespace:MVVMLight" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:view="clr-namespace:MVVMLight.View" xmlns:viewModel="clr-namespace:MVVMLight.ViewModel" Title="MainWindow" mc:Ignorable="d"> <Window.Resources> <DataTemplate DataType="{x:Type viewModel:Page1ViewModel}"> <view:Page1View /> </DataTemplate> <DataTemplate DataType="{x:Type viewModel:Page2ViewModel}"> <view:Page2View /> </DataTemplate> </Window.Resources> </Window>
因为上述ViewModel都是类似的,所以只展示其中一个的代码:
public class Model1 { public string FirstName { get; set; } public string LastName { get; set; } } public class ViewModel1 { public List<Model1> model1s { get; set; } = new List<Model1>(); public ViewModel1() { model1s.Add(new Model1 { LastName = "杨", FirstName = "超越" }); model1s.Add(new Model1 { LastName = "赵", FirstName = "丽颖" }); model1s.Add(new Model1 { LastName = "刘", FirstName = "诗诗" }); model1s.Add(new Model1 { LastName = "高", FirstName = "圆圆" }); } }
<UserControl x:Class="WpfApp1.View.UserControl1" 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" xmlns:local="clr-namespace:WpfApp1.View" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <ListBox ItemsSource="{Binding model1s}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding LastName}"/> <TextBlock Text="{Binding FirstName}" Margin="10 0 0 0"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </UserControl>
上述方法在每一次切换ViewModel的时候,都会去找它对应的View,也就意味着每一次的都会重新实例化一个View对象。因为ViewModel对象没有变化并且绑定到了View,所以View层也好像没有改变一样,但其实是重新实例化了一个对象。这里感觉有点依赖注入的意思,现在我们期望管理依赖对象的生命周期,如果没有办法做到这一点,这种方式应该也不是很友好。。。。。除非你做到了真正意义的MVVM,在View层并没有多余的信息,不然这样使用是有问题的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。