Note: The source for this project has been updated to work with Silverlight 2.
TabItems can be added to a TabControl and, with code, can be removed as well. However, unlike many (most?) tabbed interfaces (FireFox, IE7+), tabs cannot be closed by a user - unless of course you add a mechanism to let them do so. The control described in this article provides a visual way of doing that.
View the Demo
Download the source
Main Layout
We’ll start by laying out our screen. It follows the standard format of list on the left, grid splitter, content on the right.

The list is a MainMenu user control (included) that uses a ListBox for its contents. For the purposes of this demo, the menu is hard-coded. It’s also possible to dynamically load it based on the contents of a Web.sitemap.
<ListBox x:Name="mnu" Width="Auto" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="Auto" Height="20" Margin="2" MouseLeftButtonDown="OnItemMouseLeftButtonDown">
<Image Source="{Binding Icon}" Margin="2" Width="16" Height="16" />
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Stretch" FontSize="11" Height="20" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The DataTemplate has been modified so an icon can be displayed in an Image next to a TextBlock that contains the menu text itself. Rounding out the MenuEntry class is the handler.
public class MenuEntry
{
public string Name { get; set; }
public string Icon { get; set; }
public RoutedEventHandler Handler { get; set; }
}
Loading a Panel
Note the MouseLeftButtonDown event of the MainMenu is wired up so when the ListBoxItem is clicked, the MenuEntry.Handler is called back. This calls a LoadPanel method on the main Page instance.
public void LoadPanel(string header, UserControl panel)
{
if (this.IsAlreadyLoaded(header))
return;
ClosableTabItem tabItem = new ClosableTabItem();
tabItem.Close += OnClosePanel;
tabItem.Header = header;
tabItem.Content = panel;
this.pnlContent.Items.Add(tabItem);
tabItem.IsSelected = true;
}
Because we don’t want to load the same panel more than once, the IsAlreadyLoaded method is called. It simply compares the text of all TabItem headers with the text of the header about to be displayed. If there’s a match, just select the existing tab and return.
If the tab isn’t loaded yet, we create a new ClosableTabItem, set its header and its content, and select it.
Subscribe to the Close event if you want to be called back when the user clicks on the close button.

ClosableTabItem
While it was possible to create a ClosableTabItem from scratch, I decided to leverage the existing TabItem. The only enhanced functionality I’m interested in adding is a visual element (check image for the user to click) and the callback handler.
public class ClosableTabItem : TabItem
Constructing the ClosableTabItem
There are two overloaded constructors for the ClosableTabItem:
- No parameters - default to closable
- Specify if it can be closed or not
You might wonder why I would want to create a ClosableTabItem that cannot be closed - why not just use a TabItem? After all, one inherits from the other. It’s because of the styling. I wanted to change the look of TabItems so they matched ClosableTabItems. See the Styling the Controls section below to see how I was able to do that.
To build the new header, I wanted to keep the text portion and add a button. The button should be aligned to the right.

I created a two-column grid, adding a TextBlock in the first column and a Button in the second. All styling is done in generic.xaml.
Grid grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition());
grid.ColumnDefinitions.Add(new ColumnDefinition());
TextBlock textBlock = new TextBlock();
textBlock.Text = (string) newHeader;
textBlock.SetValue(Grid.ColumnProperty, 0);
grid.Children.Add(textBlock);
Button button = new Button();
button.Template = ResourceManager.GetControlTemplate("ClosableButtonTemplate");
button.Click += OnClose;
button.SetValue(Grid.ColumnProperty, 1);
grid.Children.Add(button);
base.OnHeaderChanged(oldHeader, grid);

When a TabItem’s header is changed, the method OnHeaderChanged(object oldHeader, object newHeader) that can be overridden is called. Because the ClosableTabItem may, in fact, not be Closable, the default action is to just called the base class without constructing the grid:
If it actually is Closable, the code above is called.
Failed attempt flashback: For my first attempt, I put the TextBlock and Button in a horizontal StackPanel, so the button was right-aligned by default. I found it didn’t look right though when too many tabs were opened. The tabs wrapped but got wider, so the close button ended up in the middle of the tab, not on the right. I exchanged the StackPanel for a Grid which provided the desired effect.

Handling the OnClose event
There are three things the ClosableTabItem needs to manage when the user closes it:
- Removes itself from the parent TabControl.To do this, it has to know who its parent is. The GetParentTabControl() method searches recursively to find a TabControl up the Parent chain. For safety’s sake, if a parent TabControl is never found (not very likely), there’s nothing for the ClosableTabItem to remove itself from, so nothing is done. Otherwise, it removes itself from the TabControl’s Items collection.
- Notify the programmer if they subscribed to the Close event.Simply check to see if the handler is null. If it isn’t null, call it.
- Nullify its Content.
private void OnClose(object sender, RoutedEventArgs e)
{
TabControl tabControl = GetParentTabControl(this);
if (tabControl != null)
tabControl.Items.Remove(this);
if (this.Close != null)
this.Close(this, e);
this.Content = null;
}
private static TabControl GetParentTabControl(FrameworkElement tabItem)
{
FrameworkElement parent = (FrameworkElement) tabItem.Parent;
if (parent == null)
return null;
if (parent is TabControl)
return (TabControl) parent;
return GetParentTabControl(parent);
}
Styling the Controls
[Credit to Sandy Place for the styling of the TabControl and TabItem in generic.xaml].
Here is the basis from which we started:
- We didn’t want to use the out-of-the-box styling of TabControl and TabItem
- We wanted both ClosableTabItems and TabItems to look the same
- Styles cannot inherit from other styles
- We didn’t want to put the styling in Page.xaml - rather it should go in generic.xaml
Initially, we did put the styles in Page.xaml. However, not all controls were defined in Page.xaml. Those that were could reference them declaratively; for controls defined elsewhere (e.g.: the EmployeeMainPanel’s TabControl), I used the following code:
this.Template = (ControlTemplate)((FrameworkElement)Application.Current.RootVisual).Resources["SlateTabItemStyle"];
I tend to like things to be consistent, so I moved the styles to generic.xaml so loading of the styles would all be done in code. After doing so however, this technique no longer worked. Apparently styles defined in generic.xaml are not accessible from RootVisual. However, a little research turned up a technique that proves very useful.
[Credit to Azret Botash (Developer Express) from the following article:] http://community.devexpress.com/blogs/theonewith/archive/2008/07/29/silverlight-more-then-one-default-template-in-generic-xaml.aspx
First, define the ControlTemplates (not Styles) in generic.xaml and provide a key.
<ControlTemplate x:Key="SlateTabItemStyle" TargetType="ex:TabItem">
<Grid x:Name="Root" Margin="3,2,0,1">
<Grid.Resources>
etc.
The ResourceManager class (included) is then used to load resources defined in any xaml file. This means you can define multiple resource dictionaries and load from any one of them. Because each ResourceDictionary can be defined in a separate xaml file, I suppose you could use this technique to define theme files. If you specify a null dictionaryName when loading a ResourceDictionary, it will default to generic.xaml. ResourceDictionaries are loaded into a static Dictionary cache so they’re only loaded once.
In order to use the template, I added the static GetControlTemplate() method. There are 2 overloads:
- GetControlTemplate(string key) - defaults to looking in the generic.xaml ResourceDictionary
- GetControlTemplate(string dictionaryName, string key) - looks in the dictionary of your choosing
In the constructor of the ClosableTabItem, the Template is loaded from generic.xaml:
this.Template = ResourceManager.GetControlTemplate("SlateTabItemStyle");
If you wanted to use the 2nd overload:
this.Template = ResourceManager.GetControlTemplate("generic.xaml", "SlateTabItemStyle");
Similarly, the ClosableTabItem’s close button’s Template is applied during the OnHeaderChanged event and the TabControl itself is styled whenever the Page or panel on which it is defined are loaded:
// Close button
Button button = new Button();
button.Template = ResourceManager.GetControlTemplate("ClosableButtonTemplate");
button.Click += OnClose;
// Loading the Page
private Page()
{
InitializeComponent();
this.pnlContent.Template = ResourceManager.GetControlTemplate("SlateTabControlStyle");
}
// Loading the EmployeeMainPanel
public EmployeeMainPanel()
{
InitializeComponent();
this.InitializeControls();
this.tabControl.Template = ResourceManager.GetControlTemplate("SlateTabControlStyle");
}
Finally, you can see (from loading both the Page and the EmployeeMainPanel) that it’s possible to nest TabControls, mixing and matching Closable and non-ClosableTabItems.

Summary
The code is free - if you find a use for it, enjoy. Feel free to enhance it.
Regards,
Doug