TreeTabControl. A Tree of Tab Items






4.67/5 (10 votes)
Handling a tree of customized tab items
Introduction
This project is based in a WPF User Control which is composed of two main controls: a TreeView
and a TabControl
.
The idea came up when I was investigating about how to customize the Tab items modifying their headers' appearance by adding the typical button for closing themselves. Furthermore, I was developing a project where I needed TabItem
s that could be containers of more TabItem
s, like another TabControl
. The reason is because the control contains the TreeView
. With the TreeView
, one can view the tree of TabItem
s displayed and get a quicker route to handle them.

Background
TreeTabControl
is the User Control that we must use it in our own project. The property calledSelectedTabItem
gets the currentTreeTabItem
that is in use. By using the property calledIsTreeExpanded
or the methodsHideTree()
andShowTree()
we can hide or show theTreeView
.TreeTabItem
is a object that inherits fromTabItem
and implements theIDisposable
interface. This object has been created in order to make easier the creation ofTabItem
s with the customized header. Through its constructor, we can create theTreeTabItem
s with the button for closing themselves whether we want or not. TheHeader
property has been altered and just accepts a User Control calledTabHeader
. That user control is responsible to give theTabItem
that appearance by adding the closing button and the label with the name of theTabItem
.TreeTabItemGroup
is an object that inherits fromTreeTabItem
. When that object is created, it contains aTabControl
in order to be able to hold moreTreeTabItem
s.TreeItem
is an object that inherits fromTreeViewItem
. The main feature that this object has is that it contains a pointer for theTreeTabItem
linked to the object. Then, depending of the type of theTreeTabItem
linked and its features, theTreeItem
created will be able to add the correspondingContextMenuItem
s. For instance, if theTreeTabItem
linked can be closed and its type is aTreeTabItemGroup
-can hold a collection of items- it will add the followingContextMenuItem
s: for closing theTreeTabItem
and itself; for expanding the collection of items that it has; for collapsing the collection; for showing the linkedTreeTabItem
. In case theTreeTabItem
could not be closed, it would not contain theContextMenuItem
for closing the tab. ThoseContextMenuItem
s can be customized by adding our icons. This I will explain in another section.TreeTabConfig
is the object responsible for getting the configuration of the control. As a lot of you already know, the configuration files that .NET offers can only be used at the main project that is running. Let me explain a bit better. If we have a project which needs a configuration file for a User Control that it is using, that configuration info will be got from the configuration file of the main project not for the configuration file of the User Control. So, I wanted to get more flexibility.TreeTabConfig
will get the information included in its configuration file inside the folder where the library is running. In that file, one can define the path where the icons of theContextMenuItem
s can be found, or whether we want to disable those menus with icons. Of course, more configuration keys can be added there. This object contains a couple of methods in order to get the resizedImage
object from the icon file.DisposingMethod
. Well, maybe this class should not be included here, but finally I decided to leave it. The purpose of that class was to execute a method declared outside theTreeTabControl
once aTreeTabItem
is collected by the garbage collector, in other words, when it is removed from the memory. Perhaps someone might find some utility for it. I wanted to get the control when aTreeTabItem
is closed and disposed from the collection and I wanted to execute a method when it happens. Since WPF does not support the event for disposing a object, I found this way to do it. It is easy, basically it works in the following way:TreeTabItem
implementsIDisposable
. When the methodClose()
is called, it registers itself for the next garbage's recollection.TreeTabItem
has a destructor. When the destruction of the object is happening, the object checks whether its propertyDisposingMethod
is notnull
. In that case, it will execute the reflected method in theDisposingMethod
object through theExecute
method. Also, notice that the content of theTreeTabItem
must implement theIDisposable
interface because when theTreeTabItem
is closed, it will call to theDispose()
method of its content. So, if we are interested in that, when our tab is closing itself and we want that an event happens in our content's control, we can prepare theDispose()
method for do it. Or in case the method is outside the content's control, we can use the classDisposingMethod
.
Using the Code
Adding the TreeTabControl to your Application
First of all, you must add the reference of the library in your project. Go to “References-> Add Reference...-> Browse” and select the library. Once the library added, we must declare the assembly at the header of our application creating the namespace used for the control. E.g.:
<window margin="0,0,0,0" title="Window1"
xmlns:custom="clr-namespace:TreeTab;assembly=TreeTab"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:class="TreeTabTest.Window1">
Once declared, we can add the control. E.g:
<custom:TreeTabControl Name="treeTab" IsTreeExpanded="True">
Hiding and Showing the TreeView
As I before said, one can show and hide the TreeView
if one thinks it is useless. It is quite simple, we just have to toy with the parent grid's columns, removing and creating the column and setting the visibility of the control.
public void HideTree()
{
if (this.isTreeExpanded)
{
this.treeView.Visibility = Visibility.Collapsed;
this.ContentGrid.ColumnDefinitions.RemoveAt(0);
this.isTreeExpanded = false;
}
}
public void ShowTree()
{
if (!this.isTreeExpanded)
{
ColumnDefinition col = new ColumnDefinition();
col.Width = new GridLength(150);
this.ContentGrid.ColumnDefinitions.Insert(0, col);
this.treeView.Visibility = Visibility.Visible;
this.isTreeExpanded = true;
}
}
Adding a Tab Without Closing Button on its Header
private void btnAddTab1_Click(object sender, RoutedEventArgs e)
{
this.treeTab.AddTabItem("one", "Main Tab", false, TreeItem.TREEITEM_TYPE.MAIN);
}
Adding Three Tab to Another Parent Tab
Notice that the object called <treetab> is the instance. When adding a new <treetabitem> to the collection, notice that its <id> must be unique. If the control finds out that the new <id> you are trying to use is already in use, the new TreeTabItem
will be ignored and will not be added. E.g:
private void btnAddTab3_Click(object sender, RoutedEventArgs e)
{
TreeTabItemGroup tParent = (TreeTabItemGroup) this.treeTab.GetTabItemById("two");
TreeTabItem tItem = this.treeTab.AddTabItem
("tree", "Child Group 1", true, TreeItem.TREEITEM_TYPE.MAIN, tParent);
TreeTabIGroupGrid grid = new TreeTabIGroupGrid();
grid.Children.Add(new Example());
this.treeTab.SetTabContent(tItem, grid);
this.treeTab.AddTabItem("four", "Child Group 2",
true, TreeItem.TREEITEM_TYPE.MAIN, tParent);
this.treeTab.AddTabItem("five", "Child Group 3",
true, TreeItem.TREEITEM_TYPE.MAIN, tParent);
}
Linking Icon Files to the ContextMenuItem
When you want to add a new <contextmenuitem> to the collection and you wish that the item is displayed with its custom icon, you must create with the same <id> key you will use in the configuration file section. E.g.:
<configSections>
<section name="ContextMenuIcons"
type="System.Configuration.SingleTagSectionHandler" />
</configSections>
<ContextMenuIcons>
<icon id="Close" fileName="LinkBack.ico"/>
<icon id="Expand" fileName="Add.ico"/>
<icon id="Collapse" fileName="Remove.ico"/>
<icon id="Show" fileName=""/>
</ContextMenuIcons>
History
- 28th March, 2010: Initial post