65.9K
CodeProject 正在变化。 阅读更多。
Home

在 WPF TabControl 中切换选项卡时保持 Visual Tree (优化版)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (9投票s)

2012 年 4 月 9 日

CPOL

4分钟阅读

viewsIcon

53293

downloadIcon

2659

这是“在 WPF TabControl 中切换标签页时持久化视觉树”的一个替代方案。

引言

很久以前,我偶然参与了一个项目,该项目使用 TabControl 在视图之间切换。一切都很顺利,除了在视图之间切换花费的时间太长。每次切换视图时,都会重新创建整个视觉树。快速搜索后,我找到了 Jason Ching 写的这篇精彩文章:在 WPF TabControl 中切换标签页时持久化视觉树。它正好满足了我的需求。我开始使用他的代码,性能得到了显著提升,但仍有改进的空间。本文将介绍如何在现有优秀代码的基础上进行改进。

背景

用简单的几句话来说,Jason 实现的思路是这样的:创建一个行为(behavior),将其附加到 TabControl 上,使其能够等待新的源项被创建(或者整个新的 ItemsSource 容器被附加)。一旦创建了新项,该行为就会创建一个 TabItem 实例,并将其包装在源项中。这个简单的技巧可以防止 TabControl 丢弃 TabItem 的实例,从而持久化 Tab 的视觉树。

实现

该行为由三个类实现:PersistTabBehaviorPersistTabItemsSourceHandlerPersistTabSelectedItemHandler

  • PersistTabBehavior – 处理附加属性,并维护两个静态字典,其中 Tab 控件的实例与 ItemsSource 和 SelectedItem 处理程序相关联。
  • PersistTabSelectedItemHandler – 将内部 TabControl 选择转换为外部选择。
  • PersistTabItemsSourceHandler – 处理与数据源项关联的 TabItem 的创建和操作。

有关更多详细信息,请参阅 原始文章

改进

我首先想到可以提高性能的方法是摆脱字典及其关联的查找过程。取而代之的是,该行为的实例应该同时持有对 TabControl 和 itemsource 的引用。

我个人认为,为一个简单的控件行为使用三个不同的类有点小題大作。如果所有引用都保存在一个地方,那么访问一个对象实例比访问多个对象实例要容易得多,所以我将所有这些类合并成了一个。

引用外部项源容器很简单;我们可以简单地将其存储在一个字段中。

挑战在于如何让 TabControl 引用正确的处理程序实例,该处理程序响应内部 TabControl.SelectedItem 属性的变化。TabControl.SelectedItem TabControl 类的一个 附加属性。因此,我们可以通过使用 WPF Binding 来解决引用问题,毕竟 Binding 就是为此目的而设计的。Binding 包含对目标属性实例和源实例的引用。这使得我们只需检查 Binding 对象即可检索到正确的实例。这有效地消除了对字典的需求。

现在,任何时候将 TabItemGeneratorBehavior.SelectedItemPropertyTabItemGeneratorBehavior.ItemsSourceProperty 附加到控件时,我们都会创建 TabItemGeneratorBehavior 的实例,并使用相应对象的引用初始化字段 _innerSelection_tabControl_itemsSource,附加到所需事件等。

同时,我们在 TabControl.SelectedItem 和行为的公共属性 TabItemGeneratorBehavior.SelectedTabItem 之间创建绑定。

_tabControl = tab;
_tabControl.Loaded += OnTabLoaded;
_tabControl.SetBinding(TabControl.SelectedItemProperty,
                       new Binding("SelectedTabItem") { Source = this });

在此之后,该行为的操作与原始行为完全相同。

Using the Code

我提供了一个示例项目来演示该行为的功能。

屏幕被分割成两个 TabControls 来填充区域。左侧的控件未使用该行为,而右侧的控件使用了该行为。选项卡视图区域显示了与活动选项卡关联的 TabItem 实例的唯一 ID。

正如你在左侧看到的,TabControl 重用了 TabItem 类的同一个实例来显示所有项。因此,每次显示新选项卡时,该选项卡上的所有视觉项都会被丢弃,并为选定的项创建新的视觉元素。

在右侧,每个项都有自己的 TabItem 实例。当控件在选项卡之间切换时,这些 TabItems 会被保留,视觉树不会被重新创建。

使用原始示例

如果你想尝试原始文章示例项目中的此实现,你需要按照以下步骤操作:

  1. TabItemGeneratorBehavior.cs 文件添加到项目中
  2. 修改 MainWindow.xaml 文件以包含对新行为的引用
  • <Window…> 标签中添加 xmlns:beh="clr-namespace:System.Windows.Controls"
  • <TabControl... 标签上的 b:PersistTabBehavior 替换为 beh:TabItemGeneratorBehavior

历史

  • 04/05/2012 - 首次发布。
© . All rights reserved.