Click here to Skip to main content
15,881,715 members
Articles / Desktop Programming / WPF

Add/Remove Tabs Dynamically in WPF

Rate me:
Please Sign up or sign in to vote.
4.95/5 (24 votes)
19 Nov 2012CPOL3 min read 190.8K   9.9K   30   21
This article shows how to achieve add/remove tab feature for TabControl in WPF.

Image 1

Introduction

This article shows how to create dynamic tabs in WPF (Windows Presentation Foundation) similar to the one you see in most web browsers where you have an empty tab at the end to add new tab and a close button on each tab to close the tab. There are several techniques to achieve this. Here, a List of TabItem is used to hold the tabs that are bound to the TabControl. The last item of the List is an empty tab that is used to add new TabItem in the list whenever that is selected. A DataTemplate is used for tab header to add a Delete button in each tab.

XAML

The first step is to define XAML for the TabControl. Set the ItemsSource property to {Binding} whereas the DataContext will be supplied by a List of TabItem in the code-behind. Next add the SelectionChanged event.

XML
<TabControl Name="tabDynamic" ItemsSource="{Binding}" 
SelectionChanged="tabDynamic_SelectionChanged">
</TabControl>  

Define the DataTemplate for tab header under TabControl.Resources tag and set the DataType as TabItem. Use any container to add and align a TextBlock and a delete Button. I used a DockPanel since it's easy to align delete Button to right using DockPanel.Dock property. Add Click event to the button and bind the CommandParameter property to the tab Name. This will be used to identify the tab to be deleted in the Click event handler of the button. Bind Text property of TextBlock to the Header of the TabItem. The Header that is a string value will be set in the code-behind.

XML
<DataTemplate x:Key="TabHeader" DataType="TabItem">
    <DockPanel>
        <Button Name="btnDelete" DockPanel.Dock="Right" Margin="5,0,0,0" 
         Padding="0" Click="btnDelete_Click" 
         CommandParameter="{Binding RelativeSource=
         {RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
            <Image Source="/delete.gif" Height="11" Width="11"></Image>
        </Button>
        <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=
                         {x:Type TabItem}}, Path=Header}" />
    </DockPanel>
</DataTemplate>  

This is the complete XAML that also includes the style for a TextBox control that is added to each tab:

XML
<TabControl Name="tabDynamic" ItemsSource="{Binding}" 
 SelectionChanged="tabDynamic_SelectionChanged">
    <TabControl.Resources>
        <DataTemplate x:Key="TabHeader" DataType="TabItem">
            <DockPanel>
                <Button Name="btnDelete" DockPanel.Dock="Right" 
                 Margin="5,0,0,0" Padding="0" Click="btnDelete_Click" 
                 CommandParameter="{Binding RelativeSource=
                 {RelativeSource AncestorType={x:Type TabItem}}, Path=Name}">
                    <Image Source="/delete.gif" Height="11" Width="11"></Image>
                </Button>
            <TextBlock Text="{Binding RelativeSource=
            {RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
            </DockPanel>
        </DataTemplate>
        <Style TargetType="TextBox">
            <Setter Property="VerticalAlignment" Value="Stretch"></Setter>
            <Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
            <Setter Property="AcceptsReturn" Value="True"></Setter>
            <Setter Property="TextWrapping" Value="WrapWithOverflow"></Setter>
            <Setter Property="MaxLines" Value="5000"></Setter>
            <Setter Property="ScrollViewer.VerticalScrollBarVisibility" 
             Value="Auto"></Setter>
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" 
             Value="Auto"></Setter>
        </Style>
    </TabControl.Resources>
</TabControl>  

Now I explain the code that contains the logic to add and delete TabItems.

Code-behind

In the code-behind file, define two data members. One is _tabItems that is a list of TabItem to hold the tabs and other _tabAdd that is a reference to the last TabItem that is used to add new tab dynamically.

C#
private List<TabItem> _tabItems;
private TabItem _tabAdd;    

Initialize the above data members in the contructor and set the header of the last tab as text "+" or any image you prefer. Add the first TabItem using the function AddItemItem() then bind the TabControl to the List and select first TabItem.

C#
public MainWindow() 
{ 
    try
    {
        InitializeComponent();

        // initialize tabItem array
        _tabItems = new List<TabItem>();

        // add a tabItem with + in header 
        TabItem tabAdd = new TabItem();
        tabAdd.Header = "+";

        _tabItems.Add(tabAdd);

        // add first tab
        this.AddTabItem();

        // bind tab control
        tabDynamic.DataContext = _tabItems;

        tabDynamic.SelectedIndex = 0;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}  

In the function AddTabItem() used above, add a new TabItem to the list _tabItems and returns the instance of that TabItem. Set the HeaderTemplate as the one we defined above. Give a unique name to the TabItem like "TabXX", where XX is a counter to make the name unique. You can use same unique string as the Header text too. It is important to set Header and Name properties since in the DataTemplate defined above in XAML we bound TextBlock.Text and Button.CommandParameter to these properties. Next, add controls that you want to have in your TabItem and add the TabItem in the list _tabItems. Here, I added a simple TextBox control. Also notice that the new tab is inserted right before the last tab that is used to add new tabs.

C#
private TabItem AddTabItem()
{
    int count = _tabItems.Count;

    // create new tab item
    TabItem tab = new TabItem();
    tab.Header = string.Format("Tab {0}", count);
    tab.Name = string.Format("tab{0}", count);
    tab.HeaderTemplate = tabDynamic.FindResource("TabHeader") as DataTemplate;
 
    // add controls to tab item, this case I added just a text box
    TextBox txt = new TextBox();
    txt.Name = "txt";
    tab.Content = txt;

    // insert tab item right before the last (+) tab item
    _tabItems.Insert(count - 1, tab);
    return tab; 
} 

In the implementation of the SelectionChanged event handler, check if the selected TabItem is the last one then add a new TabItem using AddTabItem() function, rebind the TabControl and select newly added TabItem. For other tabs, you can add your code, if any, for this event.

C#
private void tabDynamic_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    TabItem tab = tabDynamic.SelectedItem as TabItem;

    if (tab != null && tab.Header != null)
    {
        if (tab.Header.Equals(_addTabHeader))
        {
            // clear tab control binding
            tabDynamic.DataContext = null;

            // add new tab
            TabItem newTab = this.AddTabItem();

            // bind tab control
            tabDynamic.DataContext = _tabItems;

            // select newly added tab item
            tabDynamic.SelectedItem = newTab;
        }
        else
        {
            // your code here...
        }
    }
}

Finally, implement the Click event handler of the delete button that was added the DataTemplate. Here, identify the tab to be deleted using CommandParameter that in turn gives the TabItem.Name. You can easily find this tab in TabControl.Items collection using linq. Validate that this tab is not the only tab left in the list, then after confirmation, delete the tab from the list _tabItems and rebind the TabControl. If an active tab is deleted, then select the first tab after rebinding, otherwise select the tab that was selected before deletion.

C#
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
    string tabName = (sender as Button).CommandParameter.ToString();

    var item = tabDynamic.Items.Cast<tabitem>().Where
               (i => i.Name.Equals(tabName)).SingleOrDefault();

    TabItem tab = item as TabItem;

    if (tab != null)
    {
        if (_tabItems.Count < 3)
        {
            MessageBox.Show("Cannot remove last tab.");
        }
        else if (MessageBox.Show(string.Format
        ("Are you sure you want to remove the tab '{0}'?", tab.Header.ToString()),
            "Remove Tab", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
        {
            // get selected tab
            TabItem selectedTab = tabDynamic.SelectedItem as TabItem;

            // clear tab control binding
            tabDynamic.DataContext = null;

            _tabItems.Remove(tab);

            // bind tab control
            tabDynamic.DataContext = _tabItems;

            // select previously selected tab. if that is removed then select first tab
            if (selectedTab == null || selectedTab.Equals(tab))
            {
                selectedTab = _tabItems[0];
            }
            tabDynamic.SelectedItem = selectedTab;
        }
    }
}

History

  • 18th November, 2012: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Enquizit Inc.
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to change the background of the tabitems Pin
Hhdhf syhhd4-Feb-21 12:38
Hhdhf syhhd4-Feb-21 12:38 
AnswerRe: How to change the background of the tabitems Pin
Jo_vb.net30-Jul-22 6:50
mvaJo_vb.net30-Jul-22 6:50 
Questionvb.net wpf Pin
Member 1407575520-Jun-19 19:39
Member 1407575520-Jun-19 19:39 
BugIs tab.Content = txt; a hack? not working otherwise. Pin
Adi TheComputerGuy30-Jun-18 4:19
professionalAdi TheComputerGuy30-Jun-18 4:19 
QuestionError in IF loop Pin
Member 1115057717-Jan-18 19:02
Member 1115057717-Jan-18 19:02 
Questioncan you please give me this code in VB.net Pin
karim sorathiya24-Oct-16 23:43
karim sorathiya24-Oct-16 23:43 
Questionclose button on tabitem for tabcontrol in wpf Pin
karim sorathiya24-Oct-16 4:53
karim sorathiya24-Oct-16 4:53 
QuestionHow to add a xaml file instead of textBox? Pin
Elaheh Ordoni6-Apr-16 21:10
Elaheh Ordoni6-Apr-16 21:10 
BugBug in tabDynamic_SelectionChanged Pin
duyanhphamkiller4-Jan-15 16:46
duyanhphamkiller4-Jan-15 16:46 
QuestionWhat about Tab Content? Pin
Member 1054494629-May-14 4:06
Member 1054494629-May-14 4:06 
SuggestionImplement this little function to get correct tab headers after removing tab item. Pin
sadovetzki_s.10-May-14 5:07
sadovetzki_s.10-May-14 5:07 
QuestionProblem when Duplicate Tab Names are added Pin
Go Kool4-Mar-14 23:58
Go Kool4-Mar-14 23:58 
AnswerRe: Problem when Duplicate Tab Names are added Pin
Michel Renaud19-Nov-15 3:54
Michel Renaud19-Nov-15 3:54 
QuestionPERFECT Pin
Tony Jiwoo Park26-Nov-13 10:29
Tony Jiwoo Park26-Nov-13 10:29 
QuestionGreat work..! but still confused a little.. Pin
Member 1014061416-Sep-13 19:25
Member 1014061416-Sep-13 19:25 
QuestionChildren tabs Pin
vivek saurav15-Jul-13 23:23
vivek saurav15-Jul-13 23:23 
Questionload another form into tab Pin
Member 99706427-Apr-13 3:20
Member 99706427-Apr-13 3:20 
AnswerRe: load another form into tab Pin
molidort17-Jun-13 22:53
molidort17-Jun-13 22:53 
GeneralMy Vote of 5 Pin
Kamran Bilgrami19-Nov-12 11:22
Kamran Bilgrami19-Nov-12 11:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.