Closeable Tabs in WPF Made Easy

There are a number of good articles out there about creating closeable tabs in WPF, but they’re very complicated. It shouldn’t be that hard! So, I’m going to try to break it down and make it easy for you.

Here’s what we’re going to do:

  1. Create a new user control for the tab close button
  2. Create a new class that inherits from TabItem
  3. Try it out!

Create a new user control for the tab close button

Add a new UserControl to your project. Since it’s a full-blown UserControl, you can easily style the button however you’d like and add whatever additional code-behind logic you need. The important thing about the control is that it raises a “Close” event that can be handled in our custom TabItem control. You can get fancy with your button, but I wanted to keep it simple for this example so I just used a red button with a white X drawn on it.

XAML:

<UserControl x:Class="adamprescott.net.TabbedDocuments.TabCloseButton"
             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="300" d:DesignWidth="300">

    <Button Click="OnClick" Background="Red">
        <Path Data="M1,9 L9,1 M1,1 L9,9" Stroke="White" StrokeThickness="2" />
    </Button>
    
</UserControl>

Code-behind:

namespace adamprescott.net.TabbedDocuments
{
    using System;
    using System.Windows;
    using System.Windows.Controls;

    public partial class TabCloseButton : UserControl
    {
        public event EventHandler Click;

        public TabCloseButton()
        {
            InitializeComponent();
        }

        private void OnClick(object sender, RoutedEventArgs e)
        {
            if (Click != null)
            {
                Click(sender, e);
            }
        }
    }
}

Create a new class that inherits from TabItem

Add a new class to your project that derives from TabItem. My class has a single method, SetHeader. This method accepts the desired header content as an argument and adds it to a collection with our custom close button.

namespace adamprescott.net.TabbedDocuments
{
    using System.Windows;
    using System.Windows.Controls;

    public class CloseableTabItem : TabItem
    {
        public void SetHeader(UIElement header)
        {
            // Container for header controls
            var dockPanel = new DockPanel();
            dockPanel.Children.Add(header);

            // Close button to remove the tab
            var closeButton = new TabCloseButton();
            closeButton.Click +=
                (sender, e) =>
                {
                    var tabControl = Parent as ItemsControl;
                    tabControl.Items.Remove(this);
                };
            dockPanel.Children.Add(closeButton);

            // Set the header
            Header = dockPanel;
        }
    }
}

Try it out!

Now that all the hard stuff is done, let’s make a sample application to test it out. I have a simple Window with a Button and a TabControl. When the button is clicked, a tab is added with a TextBlock header and a TextBlock content. Each tab can be closed by clicking its close button.

XAML:

<Window x:Class="adamprescott.net.TabbedDocuments.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    
    <DockPanel>
        <Button DockPanel.Dock="Top" Click="OnPlusTabClick">+Tab</Button>
        <TabControl Name="uxTabs">
        </TabControl>
    </DockPanel>
    
</Window>

Code-behind:

namespace adamprescott.net.TabbedDocuments
{
    using System.Windows;
    using System.Windows.Controls;

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnPlusTabClick(object sender, RoutedEventArgs e)
        {
            // Create the header
            var header = new TextBlock { Text = "Tab!" };
            
            // Create the content
            var content = new TextBlock
            {
                Text = string.Format("Tab numero {0}-o", 
                    uxTabs.Items.Count + 1)
            };

            // Create the tab
            var tab = new CloseableTabItem();
            tab.SetHeader(header);
            tab.Content = content;

            // Add to TabControl
            uxTabs.Items.Add(tab);
        }
    }
}

See? It didn’t have to be that hard!