How to add a Close button to a WPF TabItem
This article shows how to add a Close button to a WPF TabItem. This Close button will only show on the selected tab.
Introduction
During a recent project, I had a need for a "Close button" on tabs, similar to that in Visual Studio 2010. While trying to find out how to do this, I found several examples on the Internet. Many of these examples used header templates for the TabItem
that used nothing but XAML or XAML mixed with some C# code. Other examples had extra features, like making the tab header a different shape, which I did not need. Although I did have a header template that sort of worked the way I wanted it to, it wasn't perfect. And, since I am not that comfortable with XAML anyways (as I come from a WinForms background), I decided to go another route. I know there are probably simpler ways to accomplish this, but the following solution felt simple and straightforward to me, and hopefully someone else finds it useful.
My tab has several requirements:
- The Close button should only show on the currently selected tab - not on all tabs.
- If a tab is not currently selected, but you move your mouse over the tab, the close button should appear. When the mouse leaves that tab, the button should disappear again.
- When the mouse moves over the Close button ("X"), the color of the "X" should turn red and a tooltip that says "Close" should appear. When the mouse leaves the button, the "X" should turn back to black.
- When the Close button is clicked, the tab should close (obviously).
- The final requirement is that my tab should show the entire title/description – not just a portion of it. So the size of the tab header should be able to grow and shrink to the title.
To accomplish this task, we need to create two items:
- A simple UserControl that contains a
Label
and aButton
. - A custom
TabItem
with a few overridden methods to handle the showing and hiding of the Close button.
Using the Code
The first item we need to create is the UserControl:
- Create a new UserControl and call it
CloseableHeader
. - Add a
Label
to this control and call itlabel_TabTitle
. - Add a
Button
to this control and call itbutton_close
. - Set the style of the button to
ToolBar.ButtonStyleKey
. - Set the
Text
(content) of the button to X.
Here is the complete XAML code for this new UserControl:
<UserControl x:Class="CloseableHeader"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="23" d:DesignWidth="81" Margin="0">
<Grid>
<Button Content="X" Height="19" HorizontalAlignment="Right" Margin="0,3,4,0"
Name="button_close" VerticalAlignment="Top" Width="20" FontFamily="Courier"
FontWeight="Bold" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
FontStretch="Normal" Visibility="Visible"
FontSize="14" Padding="0" ToolTip="Close"/>
<Label Content="TabItem" Height="23" HorizontalAlignment="Left"
Margin="4,1,0,0" Name="label_TabTitle" VerticalAlignment="Top"
FontFamily="Courier" FontSize="12" />
</Grid>
</UserControl>
The second item we need to create is the custom TabItem
:
- Create a new class called
ClosableTab
which inherits fromTabItem
. - Create the constructor.
- Create a property for setting the “title” of a tab. As shown below, we will simply update the
Label
in the UserControl. - Next, we will do four overrides to control the visibility of the Close button on the tab.
- Next, we need to handle several of our new UserControl’s events. These event handlers will control the color of the “X” on the button, the width of the UserControl (showing the entire title), and the Close button Click event to close the tab.
class ClosableTab : TabItem
{
}
Here we will create a new instance of the new UserControl CloseableHeader
that we created above. We will then assign it to the tab header as shown below. This means that when the TabItem
is displayed, it will show our new UserControl on the header. This is exactly what we need as it will display a label and our Close button.
// Constructor
public ClosableTab()
{
// Create an instance of the usercontrol
closableTabHeader = new CloseableHeader();
// Assign the usercontrol to the tab header
this.Header = closableTabHeader;
}
/// <summary>
/// Property - Set the Title of the Tab
/// </summary>
public string Title
{
set
{
((CloseableHeader)this.Header).label_TabTitle.Content = value;
}
}
When the tab is selected – show the Close button like so:
// Override OnSelected - Show the Close Button
protected override void OnSelected(RoutedEventArgs e)
{
base.OnSelected(e);
((CloseableHeader)this.Header).button_close.Visibility = Visibility.Visible;
}
When the tab is deselected – hide the Close button like so:
// Override OnUnSelected - Hide the Close Button
protected override void OnUnselected(RoutedEventArgs e)
{
base.OnUnselected(e);
((CloseableHeader)this.Header).button_close.Visibility = Visibility.Hidden;
}
When the mouse is over a tab – show the Close button like so:
// Override OnMouseEnter - Show the Close Button
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
((CloseableHeader)this.Header).button_close.Visibility = Visibility.Visible;
}
When the mouse is not over a tab – hide the Close button like so:
// Override OnMouseLeave - Hide the Close Button (If it is NOT selected)
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
if (!this.IsSelected)
{
((CloseableHeader)this.Header).button_close.Visibility = Visibility.Hidden;
}
}
Add the following code to the constructor that we already created up above:
// Attach to the CloseableHeader events
// (Mouse Enter/Leave, Button Click, and Label resize)
closableTabHeader.button_close.MouseEnter +=
new MouseEventHandler(button_close_MouseEnter);
closableTabHeader.button_close.MouseLeave +=
new MouseEventHandler(button_close_MouseLeave);
closableTabHeader.button_close.Click +=
new RoutedEventHandler(button_close_Click);
closableTabHeader.label_TabTitle.SizeChanged +=
new SizeChangedEventHandler(label_TabTitle_SizeChanged);
Add the following event handlers:
// Button MouseEnter - When the mouse is over the button - change color to Red
void button_close_MouseEnter(object sender, MouseEventArgs e)
{
((CloseableHeader)this.Header).button_close.Foreground = Brushes.Red;
}
// Button MouseLeave - When mouse is no longer over button - change color back to black
void button_close_MouseLeave(object sender, MouseEventArgs e)
{
((CloseableHeader)this.Header).button_close.Foreground = Brushes.Black;
}
// Button Close Click - Remove the Tab - (or raise
// an event indicating a "CloseTab" event has occurred)
void button_close_Click(object sender, RoutedEventArgs e)
{
((TabControl)this.Parent).Items.Remove(this);
}
// Label SizeChanged - When the Size of the Label changes
// (due to setting the Title) set position of button properly
void label_TabTitle_SizeChanged(object sender, SizeChangedEventArgs e)
{
((CloseableHeader)this.Header).button_close.Margin = new Thickness(
((CloseableHeader)this.Header).label_TabTitle.ActualWidth + 5, 3, 4, 0);
}
Note: In the Close button Click event, I am simply closing the tab by calling the parent’s (a TabControl
) Items.Remove()
method. This can obviously be extended to instead raise an event. Then the program could determine elsewhere what needs to happen when the Close button is clicked. For simplicity of the demo, I am simply closing the tab.
You can now use this custom TabItem
like this:
ClosableTab theTabItem = new ClosableTab();
theTabItem.Title = "Small title";
tabControl1.Items.Add(theTabItem);
theTabItem.Focus();
Conclusion
In conclusion, we now have Close buttons on our tabs. This was accomplished by creating a simple UserControl and a custom TabItem
. This was relatively straightforward, and it is easy to extend.