Szymon Kobalczyk's Blog

A Developer's Notebook

  Home  |   Contact  |   Syndication    |   Login
  106 Posts | 6 Stories | 578 Comments | 365 Trackbacks

News

View Szymon Kobalczyk's profile on LinkedIn

Twitter












Tag Cloud


Article Categories

Archives

Post Categories

Blogs I Read

Tools I Use

One of the most common questions I've seen regarding the TabControl in Windows Forms was how to add a close button to each tab (similar to seen on tabs in Internet Explorer 7).

Although there were some solutions available the results weren't quite satisfactory and often requiring to rewrite the whole control from scratch. Recently I faced the same challenge working on the TSRI project. It turned out that in WPF this pretty straightforward task and in this article I'm going to show all the steps required to complete it.

To follow the discussion you can download the demo code first:

Download the source code

We start by creating a new custom control deriving from TabItem that implements this behavior. I'll name it CloseableTabItem.

public class CloseableTabItem : TabItem
{
    static CloseableTabItem()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CloseableTabItem),
            new FrameworkPropertyMetadata(typeof(CloseableTabItem)));
    }
}

The instruction in static constructor informs the system that this element wants to use different style than it's parent. Since we are creating the default theme for the custom control it would be defined in generic\themes.xaml.

Before we add more code let's create the control template first. With the aid of Expression Blend we can easily create a copy of the default template for TabItem control and start from there. The default template consists only of Grid and Border that wrap the ContentPresenter. We need to place additional DockPanel inside this Border to host both the Content and our close Button. Because we will reference this button from code later it's named PART_Close following the WPF naming convention. The button contains the "x" icon defined as Path element. You can see final hierarchy of elements on the image below:

The close button should only show it's border when mouse is over and it shouldn't accept keyboard focus, hence there is additional style called CloseableTabItemButtonStyle. This template consist of Border and ContentPresenter inside of a Grid. The Border is hidden by default and shows only when mouse is over the button. To be consistent with IE7 behavior I've also added triggers to change the "x" icon fill color to red when mouse is over the button or when it's pressed.

We are now ready to test these templates. To use our new control first the containing namespace must be mapped to a XML namespace. We don't need to reference the resource dictionary because it is declared as default theme for the control. Here is example markup for the main window of the application:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CloseableTabItemDemo"
    x:Class="CloseableTabItemDemo.MainWindow"
    Title="CloseableTabItem Demo" Height="300" Width="500"
    >
    <Grid>
      <TabControl Margin="5">
        <local:CloseableTabItem Header="TabItem 1"/>
        <local:CloseableTabItem Header="TabItem 2" />
        <local:CloseableTabItem Header="TabItem 3" />
        <TabItem Header="TabItem 4" />
      </TabControl>
    </Grid>
</Window>

Note that we can freely mix the regular TabItems with our custom controls. Here is the result when we run this application:

Having all the visuals in place lets switch back to Visual Studio and finish the remining code. I choose to publish the Close button click event as the a routed event on the control. Of course I could handle it directly in the code of the control but this way I have more control on how to handle it in the application (for example I could display a confirmation dialog before closing the tab). Alternatively I could also create a custom Command and bind it directly to the close button. This would allow me to declare everything in XAML markup but I think event would be easier to use in this case. So below is the declaration for the CloseTab event:

public static readonly RoutedEvent CloseTabEvent =
    EventManager.RegisterRoutedEvent("CloseTab", RoutingStrategy.Bubble,
        typeof(RoutedEventHandler), typeof(CloseableTabItem));

public event RoutedEventHandler CloseTab
{
    add { AddHandler(CloseTabEvent, value); }
    remove { RemoveHandler(CloseTabEvent, value); }
}

To raise the event I need first to attach a handler to the Button's Click event. I can do it easily by overriding the ... method. This is where I use the PART_Close name mentioned earlier to find the button declared in template by using the GetTemplateChild method. The event handler simply raises the new event.

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    Button closeButton = base.GetTemplateChild("PART_Close") as Button;
    if (closeButton != null)
        closeButton.Click += new System.Windows.RoutedEventHandler(closeButton_Click);
}

void closeButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
    this.RaiseEvent(new RoutedEventArgs(CloseTabEvent, this));
}

So the only thing left is to actually handle this event. This will be done in the MainWindow's code behind. Normally I would have to attach handlers for this event to each tab I created but since this is a routed event (with bubble strategy) I can also attach it once on any of it's parents (up to the Window itself). Closing the tab is done by finding the parent TabControl removing the source tab from it's Items collection.

public partial class MainWindow : System.Windows.Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.AddHandler(CloseableTabItem.CloseTabEvent, new RoutedEventHandler(this.CloseTab));
    }

    private void CloseTab(object source, RoutedEventArgs args)
    {
        TabItem tabItem = args.Source as TabItem;
        if (tabItem != null)
        {
            TabControl tabControl = tabItem.Parent as TabControl;
            if (tabControl != null)
                tabControl.Items.Remove(tabItem);
        }
    }
}

That's all we need to do. Now we have customized tab items with fully working tab control. And as you can see this is relatively easy to implement so nothing prevents adding more buttons or other elements on the tabs. For example in the TSRI project we have additional button that opens the tab contents as floating window. 

posted on Sunday, April 8, 2007 4:48 PM

Feedback

# Links (4/8/2007) 4/25/2007 9:40 PM Member Blogs
.NET If Visual Studio takes ages to load Windows Workflow Foundation Web Workflow Approvals Starter Kit

# re: WPF TabItems With Close Button 8/15/2007 12:08 AM Bryan Livingston
Thanks. This came in handy.

# re: WPF TabItems With Close Button 8/31/2007 9:54 PM Chuck McKinnon
Thanks -- very useful, and educational to walk through your XAML. Just one typo correction, near the top of this page: the default theme will be in themes/generic.xaml, rather than generic/themes.xaml.

# re: WPF TabItems With Close Button 9/3/2007 8:30 PM Szymon
Chuck,
I'm glad my post was helpful for you and thanks for pointing out the error - it's already corrected.

# re: WPF TabItems With Close Button 11/29/2007 3:44 PM Sean Cullinan
Thanks Szymon!

For anyone having problems translating the code to VB I posted how I did it on my blog at http://www.blendblog.net/Blendblognet/tabid/36/EntryID/7/Default.aspx.

# re: WPF TabItems With Close Button 12/10/2007 7:36 PM Bryan
I tried your code in my project, and I cannot get the tabs to show up. If I comment out the line that changes the style it works like an old tab. I can't see anything wrong? Maybe I am missing something else?

# re: WPF TabItems With Close Button 12/10/2007 7:58 PM Bryan
Ok - so the problem is I tried to move the control style xaml into a directory called "Resources\Styles" and renamed it to "CloseableTabItem.xaml" - apparently it must be in a directory called "themes" and it must be named "generic.xaml". I don't see anywhere in the code that says it must be located here or that name but it will only work like that. I'd like to find a way to change this as it would be the only file outside of my directory structure for my project.

# re: WPF TabItems With Close Button 12/10/2007 9:24 PM Bryan
Ok - App.xaml file:

<Application x:Class="WorldVibeCoreFrameworkTool.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary Source="Resources/Styles/ClosableTabItem.xaml" />
</Application.Resources>
</Application>

Works like a champ!

# re: WPF TabItems With Close Button 12/17/2007 8:28 PM Jonathan
I don't get it. Did you create this generic.xaml file by hand, or did you use blend to do it? When I open up the solution in blend i don't see anywhere to edit the PART_close template. All I see is generic.xaml and MainWindow.xaml. when i try to open up generic.xaml, it says "generic.xaml cannot bo edited in the design view. To edit resources, select from the Resources panel."

I am really new to wpf and any more details would be great. Thanks.

# re: WPF TabItems With Close Button 12/17/2007 10:22 PM Jonathan
Was the original XAML for the CloseableTabItem created and then pasted into the generic.xaml ResourceDictionary file?

# re: WPF TabItems With Close Button 12/18/2007 12:17 PM Horst Klein
Hi sound great. But cant test it. The Link to your Downloadserver is not available.
can you send me the sample.

Best regards
Horst

# re: WPF TabItems With Close Button 12/21/2007 1:27 PM Didier
Hi,

Same problem, link not working anymore to source code ...

Thanks
Bye
Didier

# re: WPF TabItems With Close Button 12/31/2007 11:44 AM Szymon
I've moved the file to SkyDrive - look for the new link above.

# re: WPF TabItems With Close Button 1/2/2008 6:52 PM Ronald Siegel
Thanks a lot.

Ronald S.

# re: WPF TabItems With Close Button 1/4/2008 10:06 AM Mark
Hi,
works fine, but why are you delegating
the removing of the (Custom)TabItem?

My Way:
void closeButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
TabControl tabControl = this.Parent as TabControl;
if (tabControl != null)
tabControl.Items.Remove(this);
}

Mark

# re: WPF TabItems With Close Button 1/12/2008 11:26 AM smark
I was wondering if this could be adapted to CAB's TabWorkspace. Just changing the TabSmartpartInfo doesn't seem to work though. Perhaps there is some connection here with the object builder. Any ideas.

# re: WPF TabItems With Close Button 2/7/2008 9:25 AM David Schmitt
Hi Szymon!

Great code! I can't seem to figure out how to use it in my case though, since I'm binding the TabControl's ItemSource directly to my data and I'm stumped how that should work.

Any hints there?

Regards, David S.

# re: WPF TabItems With Close Button 2/20/2008 3:12 PM Leonard Brünings
Hi,

a really nice control. The only problem I have is that the Designer cannot create an instance, if I start the program the TabControl works nicely.

Any ideas?

Best Regards,
Leonard Brünings

# re: WPF TabItems With Close Button 3/19/2008 4:48 PM Roman S. Golubin
Looks wonderful, but can I use this in conjunction with the binding to TabControl.ItemSource?

# re: WPF TabItems With Close Button 4/8/2008 1:26 PM gnobber
hi, thanks for sharing the code. Currently using it in one of my projects. But I can't close a tabItem programatically (File -> Close). Any ideas?

# re: WPF TabItems With Close Button 4/11/2008 4:06 AM Adam Selene
FYI, I had to change:

TabItem tabItem = args.Source as TabItem;

to

TabItem tabItem = args.OriginalSource as TabItem;

Perhaps it's because I'm in a UserControl class and not a Window class.


# re: WPF TabItems With Close Button 4/16/2008 12:21 PM Quang Tran Minh
I also have doubt that this will support data binding :(

# re: WPF TabItems With Close Button 4/16/2008 12:25 PM Quang Tran Minh
You guys may want to check this alternative:

http://www.codeproject.com/KB/WPF/WpfTabControl.aspx?msg=2509567#xx2509567xx

# re: WPF TabItems With Close Button 5/16/2008 3:54 PM stephanie
Hello !

Thank you for your example, i have a question ! I want to use the code outside the local project. But, when i try to set the generic.xaml file in Resource (Build Action) i have an error of parsing. I think is cause of the local link of the closeableItem class in the resourceDictionnary !
How i can apply the style without having a reference of the type of the class in my resourceDictionnary ?

Thank you so much for helping me !!!

# re: WPF TabItems With Close Button 6/13/2008 11:33 AM Gareth
You could also add a code behind file for the resource dictionary and handle the close_click event and accessing the tab item through the sender templated parent.

# re: WPF TabItems With Close Button 11/1/2008 9:17 AM kreditrechner
Looks wonderful, but can I use this in conjunction with the binding to TabControl.ItemSource?

# re: WPF TabItems With Close Button 2/2/2009 8:13 PM Cristian
Thank you Szymon and also Jason. It works "beautifully"... Cheers!

# re: WPF TabItems With Close Button 2/7/2009 11:42 AM Rahul
Wouldn't it be easier just to add the button through the DataTemplate for the header (HeaderTemplate). A custom class seems to be overkill for adding a simple button which is easily achieved through the existing style framework.

# re: WPF TabItems With Close Button 2/13/2009 11:20 AM Manju
Hi
Is there a way to select previous tabitem on close of current tabitem?

# re: WPF TabItems With Close Button 3/9/2009 7:24 PM Udayakiran
In this method, the tab page is removed from items... But the object is still present in memory and poses a problem when recreating the tab page... any idea how to fix that?

# re: WPF TabItems With Close Button 3/19/2009 11:55 AM Василий
а по русски! нифига ж не понятно.

# re: WPF TabItems With Close Button 3/30/2009 2:01 AM Monica
Thank You!!! This worked really well for me with no changes. I appreciate you sharing your efforts!

# re: WPF TabItems With Close Button 4/4/2009 2:15 AM Carlos Guaneme
Great work. Thanks for sharing this.

# re: WPF TabItems With Close Button 8/6/2009 3:38 AM Francesco
Hi, Where can i find more info about "TSRI project" and "opens the tab contents as floating window". ?

# re: WPF TabItems With Close Button 11/27/2009 7:45 AM Ola
Thanks for the article, exactly what I've been looking for, for several hours.

# re: WPF TabItems With Close Button 5/6/2010 3:42 PM Daniel
What if I want to create a closeable button?

# re: WPF TabItems With Close Button 7/7/2010 8:39 AM edu
If you are using a prism or other type of IOC Try it:

<!-- SHELL -->
<Window.Resources>
<DataTemplate x:Key="ClosableTabItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabItem}}, Path=Content.Tag}"></TextBlock>
<Button Grid.Column="1"
Content="X"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabItem}}, Path=Content.DataContext.CloseTabCommand}"
Cursor="Hand"
Focusable="False"
FontFamily="Courier"
FontSize="9"
FontWeight="Bold"
Margin="5,0.5,0.5,0.5"
Padding="0"
Foreground="White"
Background="DarkRed"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
Width="22" Height="18"
>
</Button>
</Grid>
</DataTemplate>
</Window.Resources>

<TabControl Margin="12,92,12,12"
BorderBrush="Black"
BorderThickness="2"
cal:RegionManager.RegionName="{x:Static infrastructure:RegionNames.ContentRegion}" ItemTemplate="{StaticResource ClosableTabItemTemplate}">
</TabControl>

Int the ViewModel i must hate the command CloseTabCommand...

Thats it

# re: WPF TabItems With Close Button 7/12/2010 5:17 PM Anon
Thanks worked great for me :). Made relevant changes to XAML to syn with my styles and worked great

# re: WPF TabItems With Close Button 10/7/2010 9:04 PM Paul
Thanks! I'm just now learning WPF and the article and accompanying source code were very helpful.

# re: WPF TabItems With Close Button 10/13/2010 6:05 PM Tim
Now I have cosedTab, if I close the TAB, how I display back?

# re: WPF TabItems With Close Button 5/30/2011 10:49 AM Mauricio Leyzaola
Excellent explanation. I was trying to use 3rd party controls but it was too much code.
With your control I could extend the funcionallity in a matter of minutes.
Thanks a lot.

# re: WPF TabItems With Close Button 6/28/2011 8:18 AM Mike
Thanks for this but I am having trouble adding content to this control dynamically. Basically I want to write a web browser where new tabs can be added dynamically (as in recent browsers like Firefox and IE7+).

I have a child UserControl that contains a StackPanel, WebBrowser control (plus my own methods and properties etc) but I can't seem to find a way to add this control to the CloseableTabItem - for example, in the MainWindow I add a button to open a new tab and do this in the handler:

private void btnAddNewTab_Click(object sender, RoutedEventArgs e)
{
CloseableTabItem newTab = new CloseableTabItem();
WebControl myWebControl = new WebControl();

//How do I add my user control to my new CloseableTabItem????

tabControl1.Items.Add(newTab);

}

I'm fairly new to WPF so please forgive me if this a dumb question but I'm not finding parts of WPF that intuitive after doing (too) many years of Winforms!



# re: WPF TabItems With Close Button 9/11/2011 4:34 AM Álison Fernandes
What if i want this as a TabControl.ItemTemplate (so i can create tabs from a binding source) ?

# re: WPF TabItems With Close Button 9/20/2011 2:56 PM Álison Fernandes
Solution:

public class CloseableTabControl : TabControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new CloseableTabItem();
}
}


Once again, thanks for this awesome and useful post!

# re: WPF TabItems With Close Button 9/21/2011 7:40 AM chris
Very nice atricle. How do you create a new closable Tab in code behind as you do with common ones:

tabcontrol1.Items.Add("New Tab");

Thank you!

# re: WPF TabItems With Close Button 9/21/2011 7:44 AM chris
Very nice article. How do you create a new closable tab in code behind as you do with common ones?

tabcontrol1.Items.Add("New Tab");

# re: WPF TabItems With Close Button 12/13/2011 3:26 PM chris
Hey, great control, thanks
For some reason if you rename the tab, the first underline will be ignored. So if you call it "_test", it's gonna display "test". Any idea?

Regards,
chris

# re: WPF TabItems With Close Button 9/3/2012 4:09 PM Henry Minute
I don't know if this sort of thing bothers you, but the article at
http://www.codegain.com/articles/wpf/tabs/closable-tab-control-in-wpf.aspx appears to be a direct rip off of your code down to the names of the various parts/variables.

# website designing 6/20/2013 2:45 PM Jbsoftsystem
We are one of the best Web Solutions Company in Chennai, India with over 12 years of experience. We offer quality Website Designs, Web Developments, Web site Redesign, Web Promotions, ERP software Developments, Hosting and email solutions, Chennai Job consultancy, Matrimony Sites, Real Estates etc with great ROI.

# re: WPF TabItems With Close Button 8/31/2013 5:24 PM Ralph
Awesome control and nice explanation, thank you very much!

Post A Comment
Title:
Name:
Email:
Comment:
Verification: