WPF and Silverlight TabControl with scrolling and with the New Tab button.

Everyone who has ever used the TabControl class knows that it doesn’t look as expected if it has large number of tabs. If the number of tabs exceeds the size of the visible area, they are distributed among several lines. But many people prefer to place tabs in one line and scroll headers like it is implemented in Firefox.

The attempt to solve this issue was made here by one guy, but his solution is far from ideal. It doesn’t work with the ObservableCollection class, has uncommon design and doesn’t change buttons correctly. So after some work I have created my own TabControl which, I hope, works properly.


The left and right buttons change their visibility and accessibility automatically when the number of tabs or size of the control is changed. The buttons are visible if the number of tabs is large, but one of them can be disabled if it is not possible to move further.

Also there is the button with the plus sign (+). It doesn’t add new tab automatically and must be coded manually. It makes sense because I use MVVM, and it is not possible to create a new tab because each tab is related with a view model. You have to use the AddItemCommand property and bind a function with the command. I use the RelayCommand from the MVVM Light Toolkit. Also there is another implementation of the command pattern which is called DelegateCommand and it is the part of the Prism. Here is the code example:
Inside the constructor of the view model:

this.AddCommand = new RelayCommand(AddNewItemFunction);

A function of the view model:

private void AddNewItemFunction()
{
    var newItem = new TabItemViewModel();
    this.Items.Add(newItem);
    this.SelectedItem = newItem;
}

The view:

<tab:ExtendedTabControl ItemsSource="{Binding Items}"
                        SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
                        AddItemCommand="{Binding AddCommand}"

Also I’ve implemented the WPF counterpart of this control:

Silverlight source code: ExtendedTabControl.zip
Show case: Test page
WPF source code: WpfScrollableTabControl.zip

29 Responses to WPF and Silverlight TabControl with scrolling and with the New Tab button.

  1. Excellent work Mr. …Erm… Wolf. Really nice implementation.

  2. Anonymous says:

    Just what I was looking for, thanks for the article.

    Got the WPF example working nicely, took the “add tab” out and made some tweaks but have now ran into trouble moving it over to an existing C# app I wrote. Not very experienced in C# yet so I’m hoping you can post some advice on which bits (and how) to move over.

    • vortexwolf says:

      You should add the project/library with this control as a reference.
       
      I’ve implemented the example as two separate projects: the class library and the application. The class library is called `ScrollableTabControl` and contains two files: `ScrollableTabControl.cs` and `Themes\Generic.xaml`.
      There are two possible ways to add this control to your existing application:
      1. You should create a similar structure using: Add New Project -> Visual C# -> Windows -> WPF Custom Control Library. The name is important, because there are many templates with similar names like “User Control Library” or “Class Library”, but you shouldn’t use them for custom controls, you should use “WPF Custom Control Library”. Then you should change the class file and the `Generic.xaml` file and be sure that you use correct namespaces.
      2. Unpack my example somewhere and use Add Existing Project -> then find the unpacked project `ScrollableTabControl` and press the Add button.
       
      Then it will be easy to use this control in an existing application. Add Reference -> Projects -> Select the class library with the control.
      After that add the correct prefix to the header of the xaml file like this:
      xmlns:tab=”clr-namespace:ScrollableTabControl;assembly=ScrollableTabControl”
      and use it anywhere on the page using the element `tab:ScrollableTabControl`.

      If some step isn’t clear – I can describe it with more details.

      • Anonymous says:

        Clear enough to get me on the right track, thanks.

        The key was to add “Custom Control (WPF)” rather than an ordinary class. When Visual Studio 2010 creates the CustomControl1.cs file it actually fills it with approx 30 lines of comment giving step by step instructions on what to do next. Very helpful! As you say, get the namespace/x:Name etc straight and it’s a cinch after that.

        Anyway, thanks again for a great custom control (even though I stripped some of your stuff back out this time around, it was just the left/right scrolling I was after and I liked how you did it compared to the other solutions out there).

  3. Hey there,

    first of all, big thanks for making a custom control like the ExtendedTabControl. However, I’m having some troubles when using the control in a WPF-project. In SL everything works fine, but in WPF, I only get some empty space with an border which looks like a “1,0,0,1”.

    Do you perhaps know about any bugs when working with the control in WPF? Btw moved the resources from “Themes\Generic” to AppResources.

    • vortexwolf says:

      The file “Themes\Generic” is automatically created for custom controls and is recommended to use with them. But this file isn’t obligatory, you can move the style definition to the App.xaml if only the style is defined without the “x:Key” attribute.
      About appearance of the control, can you post a screenshot or send a code? Also try to run the project at this link: WpfScrollableTabControl.zip, and check whether it is displayed correctly.

  4. Anonymous says:

    I’m using your custom TabControl for static content (IE in the XAML I have several TabItems defined). The problem is when the Window containing the contents of the TabControl/TabItems isn’t drawn.

    I’ve tried tabControl1.Selected = 0; and textBlockWithinTabItem1.Focus(); etc to force it without any joy. Help!

    Also I’d like to add a CornerRadius to the tab headers, where should I add that in?

    Thanks

    • Anonymous says:

      got distracted.. meant to say “when the Window containing the TabControl is drawn, the contents of the TabControl (IE the TabItems) *isn’t* drawn (contents are drawn after an click/update event happens but I want the contents drawn from the start).

      • vortexwolf says:

        If you want to select a tab item as soon as the window is loaded, add either `tabControl1.SelectedIndex = 0` or `tabControl1.SelectedItem = tabControl1.Items[0]` to the constructor of the window after the line `InitializeComponents`. Moreover, I think that the SelectedIndex property can be set in xaml.

        To change the CornerRadius of the tab item header you should change the control template for the TabItem class (the default template can be found here). You can try to use the style without a key, but if it isn’t work, you can set the x:Key property to this style and apply it to the ItemContainerStyle property of the TabControl.

  5. Anonymous says:

    as per my post I’d already tried “tabControl1.SelectedIndex = 0″, and I had put it just after InitializeComponent();

    So, in the code I’ve tried:
    * tabControl1.SelectedItem = tabControl1.Items[0];
    * tabControl1.SelectedIndex = 0;
    * textBlockInTabItem11.Focus();

    In XAML I’ve tried:
    * IsSelected=”true”
    * Selector.IsSelected=”True”

    None of these work but… On occasion, when the PC is slow (IE HDD thrashing whilst the MainWindow is being drawn) I see the TabItem1 content flicker before it gets blanked.

    I can only conclude that you are intercepting events that have a side-effect of preventing TabItem1 being given focus programmatically… As previously mentioned once the tab is clicked or an event (such as a button press is used) then I can get the TabItem1 contents to display but that’s not satisfactory for the app behaviour.

    As a reminder, I’ve only just started off with C# & WPF so am a bit stuck on this one.

    Thanks for the pointer on the CornerRadius, I’ll see what I can do with trial and error to get that working once the bulk of the app is working. After all it’s just a “nice to have”.

    • vortexwolf says:

      I’ve created the blank project, added the library with the ScrollableTabControl class, and added the following mark-up to the MainWindow.xaml (and I left default code-behind):

              <tab:ScrollableTabControl SelectedIndex="1">
                  <tab:ScrollableTabControl.Items>
                      <TabItem Header="Item 0" Content="111" />
                      <TabItem Header="Item 1" Content="222" />
                  </tab:ScrollableTabControl.Items>
              </tab:ScrollableTabControl>
      

      Everything worked as expected: the second item was selected.

      Could you post here your xaml code? Because I don’t know how to reproduce this issue with item selection.

      • Anonymous says:

        no need! after seeing the example in your reply I went back and tried everything again. Still “broken”.

        So it occured to me that I had SelectedItem=”{Binding SelectedItem, Mode=TwoWay}” in the tabControl (which was carried over from your example app to my app in the cut & paste).

        After removing that binding the control behaves as I’d expect it to. I knew there must be an override going on somewhere.

        Thanks for taking a look and spending the time, without your reply I’d have carried on with my dirty workaround to force it to work.

      • Anonymous says:

        another dirty workaround I’ve had to do is:
        1. I’ve defined my tabItems in XAML and set them to “collapsed”.
        2. Programatically set them to visible as needed (this is sort of a Wizard for users, which takes them step by step through a form filling/process)

        problem: as the width of the tabControl is exceeded with “new” tabitems (IE tabItems are set visible) only the left hand most 10 pixels or so are visible (as if they were underneath the “scroll right” button). Manually scrolling right brings them into view.

        I’ve had to add an event handler to *each* tabItem… IsVisibleChanged=”forceScrollRight”. All that method does is call your method tabControl1.tabRightButton_Click() along with a tabControl1.UpdateLayout() for good luck.

        It works ok, but seems messy. Any advice on a more elegant fix?

        Suggestion for the future: Rather than scroll left or right a fixed amount (25 in your example, although I’ve increased that for my app) how about checking which item is just out of view (either the tabItem left if scrolling left, or the tabItem right if scrolling right), measure it’s width and scroll by that amount? Be also good to add a <> (to go straight to first/last visible tabItem).

      • Anonymous says:

        hmm, your blog has stripped something out, so I’ll describe… add two more buttons, a left one with a double (or can embed images of course).

      • Anonymous says:

        rats, it’s done it again. obviously doesn’t like “less than” and “greater than” symbols.

    • vortexwolf says:

      I already had the method for scrolling to the selected item, so I changed it in order to work with any item. Download the archive at the end of the post once again, I’ve updated it.
      Still have some issues with visibility of extra items when I move left and right, but it doesn’t look gross now, so I’ve left it so.
      I haven’t added << and >> buttons, maybe I’ll add them some other day, but actually you can implement them yourself: retrieve the first or the last tab item using the ItemContainerGenerator property or whatever and call the ScrollToItem method.
      Comments are rendered by html-markup there, so you should use &lt; to display <. Also you can add links and pictures to your comment by using other html tags.

  6. Anonymous says:

    Thanks for the latest download. All sorted, and I’ve implemented the << and >>

    Keep up the good work :-)

  7. Anonymous says:

    I had dowonlad this Project,but not run my Application(VS2010).

    Thanks,
    Philip.D

    • vortexwolf says:

      The WPF application at the end of the post works fine, at least in the MSVS 2010 Service Pack 1. As to the Silverlight project, maybe it is because of the recent release of Silverlight 5: it is necessary to download Silverlight Development Tools of the latest version.

  8. Anonymous says:

    I add ExtendedTabControl project to my solution, but it gives error ExtendedTabControl’ is a ‘namespace’ but is used like a ‘type’. After changing namespace to Extended_TabControl, it can be built successfully but get running exception “Error: Unhandled Error in Silverlight Application
    Code: 4004
    Category: ManagedRuntimeError
    Message: Set property ‘System.Windows.FrameworkElement.Style’ threw an exception.

    What is going wrong?

    • vortexwolf says:

      I think I shouldn’t use the same name for the class and namespace.
      As to your second error, find in generic.xaml or any other xaml file the line like xmlns:local=”clr-namespace:ExtendedTabControl…” and replace that namespace according to the new name.

  9. Anonymous says:

    Thanks, it is working. One more thing, how to remove add button?

    • vortexwolf says:

      Open the themes/generic.xaml file, then find inside the default style the button element with the name like AddTabButton, I don’t remember exactly, and set Visibility= Collapsed

  10. reside says:

    Hi I have tried to use this with silverlight4 but apparently that one is only for wpf it doesn’t work with silverlight. Do you have silverlight version at all?

  11. Pingback: Silverlight TabControl with data binding « Silverlight, WPF and Windows 8 Development

  12. Alejandro says:

    Nice work!!

  13. Deny says:

    nice work, but i have question, how about after we click add button it will go and show new data? i’ve tried using

    private void AddNewItemFunction()
    {
    var nextIndex = this.Items.Count + 1;
    var newItem = new TabItemViewModel(“Added tab item ” + nextIndex, “Content of the tab item ” + nextIndex, this.CloseItem);
    this.Items.Add(newItem);
    this.SelectedItem = this.Items[this.Items.Count-1];
    }

    and it always view the first data, anyone can help? :D

    • vortexwolf says:

      What is `this.SelectedItem`? If it is a plain property, you should change it so that it calls the PropertyChanged event of the INotifyProeprtyChanged itnerface.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 25 other followers

%d bloggers like this: