Bootstrap

示例:WPF中如何处理TabControl页面绑定ItemsSource切换TabItem时UI数据没有持久保存的问题

一、目的:在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个人主页-哔哩哔哩视频

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;