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 called SelectedTabItem
gets the current TreeTabItem
that is in use. By using the property called IsTreeExpanded
or the methods HideTree()
and ShowTree()
we can hide or show the TreeView
.
TreeTabItem
is a object that inherits from TabItem
and implements the IDisposable
interface. This object has been created in order to make easier the creation of TabItem
s with the customized header. Through its constructor, we can create the TreeTabItem
s with the button for closing themselves whether we want or not. The Header
property has been altered and just accepts a User Control called TabHeader
. That user control is responsible to give the TabItem
that appearance by adding the closing button and the label with the name of the TabItem
.
TreeTabItemGroup
is an object that inherits from TreeTabItem
. When that object is created, it contains a TabControl
in order to be able to hold more TreeTabItem
s.
TreeItem
is an object that inherits from TreeViewItem
. The main feature that this object has is that it contains a pointer for the TreeTabItem
linked to the object. Then, depending of the type of the TreeTabItem
linked and its features, the TreeItem
created will be able to add the corresponding ContextMenuItem
s. For instance, if the TreeTabItem
linked can be closed and its type is a TreeTabItemGroup
-can hold a collection of items- it will add the following ContextMenuItem
s: for closing the TreeTabItem
and itself; for expanding the collection of items that it has; for collapsing the collection; for showing the linked TreeTabItem
. In case the TreeTabItem
could not be closed, it would not contain the ContextMenuItem
for closing the tab. Those ContextMenuItem
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 the ContextMenuItem
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 resized Image
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 the TreeTabControl
once a TreeTabItem
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 a TreeTabItem
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
implements IDisposable
. When the method Close()
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 property DisposingMethod
is not null
. In that case, it will execute the reflected method in the DisposingMethod
object through the Execute
method. Also, notice that the content of the TreeTabItem
must implement the IDisposable
interface because when the TreeTabItem
is closed, it will call to the Dispose()
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 the Dispose()
method for do it. Or in case the method is outside the content's control, we can use the class DisposingMethod
.
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