Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Persist the Visual Tree when switching tabs in the WPF TabControl (Optimized)

0.00/5 (No votes)
9 Apr 2012 1  
This is an alternative for Persist the Visual Tree when switching tabs in the WPF TabControl.

Introduction

Once upon a time I happened to work on a project which used TabControl to switch from one view to the next. All was well except it took forever to switch between views. Each view recreated the entire visual tree every time it became active. A quick search brought me to this excellent article by Jason Ching: Persist the Visual Tree when switching tabs in the WPF TabControl. It did precisely what I needed. I started to use his code and performance increased dramatically but there was still room for improvement. This article explains what could be done to already good code to make it even better.

Background

The idea behind Jason’s implementation in simple words could be described as this: Create behaviour, attach it to the TabControl where it waits for a new source items to be created (or whole new Items Source container attached). Once new item is created the behaviour creates a TabItem instance and wraps source item in it. This simple trick prevents TabControl from discarding TabItem’s instance and persists visual tree of the Tab.

Implementation

The behaviour is implemented by three classes: PersistTabBehavior, PersistTabItemsSourceHandler, and PersistTabSelectedItemHandler.

  • PersistTabBehavior – handles attached properties and maintains two static dictionaries where instances of Tab Controls are associated with Items Source and Selected Item handlers.
  • PersistTabSelectedItemHandler – translates internal TabControl selection into external selection.
  • PersistTabItemsSourceHandler – handles creating and manipulating TabItems associated with data source items.

For more details please consult original article.

Improvements

First thing I thought would improve performance is if we could get rid of the dictionaries and lookup process associated with them. Instead the instance of the behaviour should hold reference to both the TabControl as well as reference to items source.

I also thought that three different classes for one simple control behaviour is a bit of an overkill. If all the references are held in one place it is much easier to access only one instance of the object as opposed to multiple so I’ve merged all these classes into only one.

Referring external items source container is easy; we could simply store it in one of the fields.

The challenge is in making TabControl to reference correct instance of the handler which responds to changes in internal TabControl.SelectedItem property. TabControl.SelectedItem is an attached property of the TabControl class. So we could solve reference problem by using WPF Binding, after all Bindings where designed just for that purpose. Binding contains references to both the target property instance as well as reference to source instance. This allows us to retrieve correct instance just by examining the Binding object. This effectively eliminating a need for the dictionaries.

Now any time either TabItemGeneratorBehavior.SelectedItemProperty or TabItemGeneratorBehavior.ItemsSourceProperty is attached to the control we create instance of TabItemGeneratorBehavior and initialize fields _innerSelection, _tabControl, _itemsSource with references to respective objects, attach to required events and etc.

At the same time we create binding between TabControl.SelectedItem and TabItemGeneratorBehavior.SelectedTabItem public property of the behaviour:

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

After that the behaviour operates exactly as the original.

Using the Code

I’ve provided a sample project to demonstrate behaviour's functionality.

The screen is split with two TabControls populating the area. The control on the left is not using the behaviour and control on the right does. The view area of the tab displays the unique ID of the TabItem instance that is associated with the active tab.

As you can see on the left, TabControl reuses same instance of the TabItem class to display all of the items. So each time new tab is displayed all of the visual items on that tab are discarded and new visuals for selected item are created.

On the right each item has its own instance of the TabItem. These TabItems are preserved when control switches between tabs and visual tree is not recreated.

Using the original sample

If you would like to try this implementation with sample project associated with original article you need to follow these steps:

  1. Add TabItemGeneratorBehavior.cs file to the project
  2. Modify MainWindow.xaml file to include reference to the new behaviour:
  • Add xmlns:beh="clr-namespace:System.Windows.Controls" to the <Window…> tag.
  • Replace b:PersistTabBehavior with beh:TabItemGeneratorBehavior on the <TabControl... tag.

History

  • 04/05/2012 - Initial publication.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here