Geeks With Blogs

News
View Szymon Kobalczyk's profile on LinkedIn

Szymon Kobalczyk's Blog A Developer's Notebook

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 Development , WPF | Back to top


Comments on this post: WPF TabItems With Close Button

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

# re: WPF TabItems With Close Button
Requesting Gravatar...
Thanks. This came in handy.
Left by Bryan Livingston on Aug 15, 2007 12:08 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by Chuck McKinnon on Aug 31, 2007 9:54 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Chuck,
I'm glad my post was helpful for you and thanks for pointing out the error - it's already corrected.
Left by Szymon on Sep 03, 2007 8:30 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by Sean Cullinan on Nov 29, 2007 3:44 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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?
Left by Bryan on Dec 10, 2007 7:36 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by Bryan on Dec 10, 2007 7:58 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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!
Left by Bryan on Dec 10, 2007 9:24 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by Jonathan on Dec 17, 2007 8:28 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Was the original XAML for the CloseableTabItem created and then pasted into the generic.xaml ResourceDictionary file?
Left by Jonathan on Dec 17, 2007 10:22 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Hi sound great. But cant test it. The Link to your Downloadserver is not available.
can you send me the sample.

Best regards
Horst
Left by Horst Klein on Dec 18, 2007 12:17 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Hi,

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

Thanks
Bye
Didier
Left by Didier on Dec 21, 2007 1:27 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
I've moved the file to SkyDrive - look for the new link above.
Left by Szymon on Dec 31, 2007 11:44 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Thanks a lot.

Ronald S.
Left by Ronald Siegel on Jan 02, 2008 6:52 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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
Left by Mark on Jan 04, 2008 10:06 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by smark on Jan 12, 2008 11:26 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by David Schmitt on Feb 07, 2008 9:25 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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
Left by Leonard Brünings on Feb 20, 2008 3:12 PM

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

# re: WPF TabItems With Close Button
Requesting Gravatar...
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?
Left by gnobber on Apr 08, 2008 1:26 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by Adam Selene on Apr 11, 2008 4:06 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
I also have doubt that this will support data binding :(
Left by Quang Tran Minh on Apr 16, 2008 12:21 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
You guys may want to check this alternative:

http://www.codeproject.com/KB/WPF/WpfTabControl.aspx?msg=2509567#xx2509567xx
Left by Quang Tran Minh on Apr 16, 2008 12:25 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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 !!!
Left by stephanie on May 16, 2008 3:54 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by Gareth on Jun 13, 2008 11:33 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Looks wonderful, but can I use this in conjunction with the binding to TabControl.ItemSource?
Left by kreditrechner on Nov 01, 2008 9:17 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Thank you Szymon and also Jason. It works "beautifully"... Cheers!
Left by Cristian on Feb 02, 2009 8:13 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by Rahul on Feb 07, 2009 11:42 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Hi
Is there a way to select previous tabitem on close of current tabitem?
Left by Manju on Feb 13, 2009 11:20 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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?
Left by Udayakiran on Mar 09, 2009 7:24 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
а по русски! нифига ж не понятно.
Left by Василий on Mar 19, 2009 11:55 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Thank You!!! This worked really well for me with no changes. I appreciate you sharing your efforts!
Left by Monica on Mar 30, 2009 2:01 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Great work. Thanks for sharing this.
Left by Carlos Guaneme on Apr 04, 2009 2:15 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Hi, Where can i find more info about "TSRI project" and "opens the tab contents as floating window". ?
Left by Francesco on Aug 06, 2009 3:38 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Thanks for the article, exactly what I've been looking for, for several hours.
Left by Ola on Nov 27, 2009 7:45 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
What if I want to create a closeable button?
Left by Daniel on May 06, 2010 3:42 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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
Left by edu on Jul 07, 2010 8:39 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Thanks worked great for me :). Made relevant changes to XAML to syn with my styles and worked great
Left by Anon on Jul 12, 2010 5:17 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Thanks! I'm just now learning WPF and the article and accompanying source code were very helpful.
Left by Paul on Oct 07, 2010 9:04 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Now I have cosedTab, if I close the TAB, how I display back?
Left by Tim on Oct 13, 2010 6:05 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by Mauricio Leyzaola on May 30, 2011 10:49 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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!

Left by Mike on Jun 28, 2011 8:18 AM

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

# re: WPF TabItems With Close Button
Requesting Gravatar...
Solution:

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


Once again, thanks for this awesome and useful post!
Left by Álison Fernandes on Sep 20, 2011 2:56 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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!
Left by chris on Sep 21, 2011 7:40 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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");
Left by chris on Sep 21, 2011 7:44 AM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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
Left by chris on Dec 13, 2011 3:26 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
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.
Left by Henry Minute on Sep 03, 2012 4:09 PM

# website designing
Requesting Gravatar...
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.
Left by Jbsoftsystem on Jun 20, 2013 2:45 PM

# re: WPF TabItems With Close Button
Requesting Gravatar...
Awesome control and nice explanation, thank you very much!
Left by Ralph on Aug 31, 2013 5:24 PM

Your comment:
 (will show your gravatar)


Copyright © Szymon Kobalczyk | Powered by: GeeksWithBlogs.net | Join free