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 a Button
.
- 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 it label_TabTitle
.
- Add a
Button
to this control and call it button_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 from TabItem
.
class ClosableTab : TabItem
{
}
- Create the constructor.
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.
public ClosableTab()
{
closableTabHeader = new CloseableHeader();
this.Header = closableTabHeader;
}
- Create a property for setting the “title” of a tab. As shown below, we will simply update the
Label
in the UserControl.
public string Title
{
set
{
((CloseableHeader)this.Header).label_TabTitle.Content = value;
}
}
- Next, we will do four overrides to control the visibility of the Close button on the tab.
When the tab is selected – show the Close button like so:
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:
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:
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:
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
if (!this.IsSelected)
{
((CloseableHeader)this.Header).button_close.Visibility = Visibility.Hidden;
}
}
- 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.
Add the following code to the constructor that we already created up above:
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:
void button_close_MouseEnter(object sender, MouseEventArgs e)
{
((CloseableHeader)this.Header).button_close.Foreground = Brushes.Red;
}
void button_close_MouseLeave(object sender, MouseEventArgs e)
{
((CloseableHeader)this.Header).button_close.Foreground = Brushes.Black;
}
void button_close_Click(object sender, RoutedEventArgs e)
{
((TabControl)this.Parent).Items.Remove(this);
}
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.