一、目的:在WPF开发过程中,经常用到TabControl,也会遇到类似问题,用TabControl绑定数据源ItemsSource时,切换TabItem时,UI上的数据没有持久保存,本文介绍一种处理方式,可以做到缓存页面,只在切换TabItem时Load一次,重复切换TabItem时不会重复Load
二、实现
首先介绍遇到问题的写法
<TabItem Header="TabControl页面数据没有缓存"><TabControl ItemsSource="{h:GetStudents Count=10}"><TabControl.Resources><BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /><DataTemplate DataType="{x:Type h:Student}"><Grid Loaded="Grid_Loaded"><Grid.ColumnDefinitions><ColumnDefinition Width="100" /><ColumnDefinition Width="5" /><ColumnDefinition /></Grid.ColumnDefinitions><TextBox HorizontalAlignment="Stretch" /><GridSplitter Grid.Column="1"Width="5"ResizeBehavior="PreviousAndNext" /><h:Form Grid.Column="2"SelectObject="{Binding}" /></Grid></DataTemplate></TabControl.Resources><TabControl.ItemTemplate><DataTemplate><TextBlock Text="{Binding Name}" /></DataTemplate></TabControl.ItemTemplate></TabControl></TabItem>
运行效果如下:
可以看到,Binding到后台数据的内容可以持久化保存,但是没有Binding(左侧的TextBox)或者UI上的元素(GridSplitter)的位置没有持久化保存
使用ListBox作为容器修改后的写法
<TabItem Header="TabControl持久保存页面"><TabControl ItemsSource="{h:GetStudents Count=10}"><TabControl.Resources><BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /><DataTemplate DataType="{x:Type h:Student}"><Grid Loaded="Grid_Loaded"><Grid.ColumnDefinitions><ColumnDefinition Width="100" /><ColumnDefinition Width="5" /><ColumnDefinition /></Grid.ColumnDefinitions><TextBox HorizontalAlignment="Stretch" /><GridSplitter Grid.Column="1"Width="5"ResizeBehavior="PreviousAndNext" /><h:Form Grid.Column="2"SelectObject="{Binding}" /></Grid></DataTemplate></TabControl.Resources><TabControl.ItemTemplate><DataTemplate><TextBlock Text="{Binding Name}" /></DataTemplate></TabControl.ItemTemplate><TabControl.ContentTemplate><DataTemplate><ListBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=TabControl}, Path=ItemsSource}"SelectedItem="{Binding RelativeSource={RelativeSource AncestorType=TabControl}, Path=SelectedItem}"><ListBox.ItemsPanel><ItemsPanelTemplate><Grid /></ItemsPanelTemplate></ListBox.ItemsPanel><ListBox.ItemContainerStyle><Style TargetType="ListBoxItem"><Setter Property="Visibility" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" /><Setter Property="HorizontalContentAlignment" Value="Stretch" /><Setter Property="VerticalContentAlignment" Value="Stretch" /><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ListBoxItem"><ContentPresenter /></ControlTemplate></Setter.Value></Setter></Style></ListBox.ItemContainerStyle></ListBox></DataTemplate></TabControl.ContentTemplate></TabControl></TabItem>
相比之前的有问题的写法实际上是增加了TabControl.ContentTemplate的配置,改用ListBox,使用Grid作为ListBox的 ItemsPanel,每次切换TabItem时实际上是切换ListBox中ListBoxItem的显示和隐藏,这样就可以做到切换页面的持久化保存和Load只加载一次的效果。
运行效果如下:
五、需要了解的知识点
TabControl 样式和模板 - WPF .NET Framework | Microsoft Learn
TabControl 类 (System.Windows.Controls) | Microsoft Learn
ListBox 类 (System.Windows.Controls) | Microsoft Learn
System.Windows.Controls 命名空间 | Microsoft Learn
六、源码地址
GitHub - HeBianGu/WPF-ControlDemo: 示例
GitHub - HeBianGu/WPF-ControlBase: Wpf封装的自定义控件资源库
GitHub - HeBianGu/WPF-Control: WPF轻量控件和皮肤库
七、了解更多
System.Windows.Controls 命名空间 | Microsoft Learn
https://github.com/HeBianGu
HeBianGu的个人空间-HeBianGu个人主页-哔哩哔哩视频