当前位置:   article > 正文

ContentControl的Content绑定View还是ViewModel_maui中如何实现contentcontrol一样页面切换

maui中如何实现contentcontrol一样页面切换

一、背景

在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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
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);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

上面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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

因为上述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 = "圆圆" });
     }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

三、效果

在这里插入图片描述

四、总结

上述方法在每一次切换ViewModel的时候,都会去找它对应的View,也就意味着每一次的都会重新实例化一个View对象。因为ViewModel对象没有变化并且绑定到了View,所以View层也好像没有改变一样,但其实是重新实例化了一个对象。这里感觉有点依赖注入的意思,现在我们期望管理依赖对象的生命周期,如果没有办法做到这一点,这种方式应该也不是很友好。。。。。除非你做到了真正意义的MVVM,在View层并没有多余的信息,不然这样使用是有问题的

  1. 每次切换都会重新实例化View层,消耗资源
  2. 如果View层有数据,那么每一次都会复原,不会像在ViewModel层中的数据一样复原到新实例化的View层
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号