Click here to Skip to main content
Click here to Skip to main content

Reordering TabPages inside TabControl

By , 12 Sep 2006
 

Introduction

When using TabControl in my programs, usually there is one thing users always ask for - "let me reorder those tabs". Since reordering tabs is such a logical operation, one would think that the mighty .NET 2.0 Framework has one. But no luck there. Visual Studio 2005 does have that feature, but mortals are not blessed with it.

Extending

Visual Basic programmers waited for inheritance for far too long so they tend to use it for solving every possible problem (when you have a hammer, all problems look like nails). May I say that here, the same approach will be used.

What we want to do here is to extend the standard TabControl. Inside of it, we are interested in MouseDown, MouseMove and MouseUp events. Some people like to use drag and drop here, but that seems to me like using a bomb to kill a fly.

MouseDown

Upon receiving a MouseDown event, we check if the Left button is used and if TabControl has tab selected. If those requirements are fulfilled, we initialize the SourceTabPage variable.

  If (e.Button = Windows.Forms.MouseButtons.Left) AndAlso _
        (MyBase.SelectedTab IsNot Nothing) AndAlso (Not MyBase.GetTabRect_
        (MyBase.SelectedIndex).IsEmpty) Then
    Me._SourceTabPage = MyBase.SelectedTab
  End If
  MyBase.OnMouseDown(e)

With that, we are ready for further events.

MouseMove

When the mouse is moved, one must check if reordering is in progress. If that is true, we look at what TabPage is under it. If it is not starting one - we have a show.

One of the things we must discover is which TabPage we are hovering over. Since unfortunately, we cannot use some sort of HitText, we must iterate through all TabPages and select one with our coordinates.

Private Function GetTabPageFromXY(ByVal x As Integer, ByVal y As Integer) As TabPage
  For i As Integer = 0 To MyBase.TabPages.Count - 1
    If MyBase.GetTabRect(i).Contains(x, y) Then
      Return MyBase.TabPages(i)
    End If
  Next
  Return Nothing

End Function

After that, we check which side we are hovering. It is only important if you wish to display a different cursor for each side. These two are the ones I use, but you can think of your own schema.

  Dim currRect As Drawing.Rectangle = _
        MyBase.GetTabRect(MyBase.TabPages.IndexOf(currTabPage))
  If (MyBase.TabPages.IndexOf(currTabPage) < _
        MyBase.TabPages.IndexOf(Me._SourceTabPage)) Then
    MyBase.Cursor = Cursors.PanWest
  ElseIf (MyBase.TabPages.IndexOf(currTabPage) > _
        MyBase.TabPages.IndexOf(Me._SourceTabPage)) Then
    MyBase.Cursor = Cursors.PanEast
  Else
    MyBase.Cursor = Cursors.Default
  End If

MouseUp

Here, we must know on which side we are since the new location (before or after hovering TabPage) depends on this information. This code is basically the same as the one we use for determining the cursor type.

  Dim currRect As Drawing.Rectangle = _
        MyBase.GetTabRect(MyBase.TabPages.IndexOf(currTabPage))
  If (MyBase.TabPages.IndexOf(currTabPage) < _
        MyBase.TabPages.IndexOf(Me._SourceTabPage)) Then 'bigger then
    MyBase.TabPages.Remove(Me._SourceTabPage)
    MyBase.TabPages.Insert(MyBase.TabPages.IndexOf(currTabPage), Me._SourceTabPage)
    MyBase.SelectedTab = Me._SourceTabPage
  ElseIf (MyBase.TabPages.IndexOf(currTabPage) > _
        MyBase.TabPages.IndexOf(Me._SourceTabPage)) Then
    MyBase.TabPages.Remove(Me._SourceTabPage)
    MyBase.TabPages.Insert(MyBase.TabPages.IndexOf(currTabPage) + 1, Me._SourceTabPage)
    MyBase.SelectedTab = Me._SourceTabPage
  End If

The only thing after that is clearing things up.

  Me._SourceTabPage = Nothing
  MyBase.Cursor = Cursors.Default

This is called whether we move anything or not since the user can decide to move our tab to some location where we cannot follow it.

Conclusion

This extended control offers a good solution to reordering TabPages inside a TabControl. I think that the only thing one may hold against it is updating the order on MouseUp but that decision is made for the sake of speed and code clarity. This way, it is very easy to implement changes through further extending since it uses protected OnMouseX procedures.

Hope you like it.

History

  • 12th September, 2006: Initial post

License

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

About the Author

Josip Medved
Software Developer (Senior) Siemens
Croatia Croatia
Member
.NET programmer with MCSD and MCPD status. Loves chevapi...
Medo's Home Page

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionCSharp versionmemberKendoTM7 Jun '12 - 21:36 
Excellent solution! Below I rewrote the code in csharp.
 
using System.Windows.Forms;
 
namespace DnDTabControlCS
{
    public partial class TabControlDnD : TabControl
    {
        private TabPage _SourceTabPage;
        
        public TabControlDnD()
        {
            InitializeComponent();
        }
 
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Left && SelectedTab != null && !GetTabRect(SelectedIndex).IsEmpty)
            {
                _SourceTabPage = SelectedTab;
            }
            base.OnMouseDown(e);
        }
 
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && _SourceTabPage != null)
            {
                TabPage currTabPage = GetTabPageFromXY(e.X, e.Y);
                if(currTabPage != null)
                {
                    if (TabPages.IndexOf(currTabPage) < TabPages.IndexOf(_SourceTabPage))
                        Cursor = Cursors.PanWest;
                    else if (TabPages.IndexOf(currTabPage) > TabPages.IndexOf(_SourceTabPage))
                        Cursor = Cursors.PanEast;
                    else//tab is same as current
                        Cursor = Cursors.Default;
                }
                else//no tab selected
                {
                    Cursor = Cursors.No;
                }
            }
            else
            {
                Cursor = Cursors.Default;
            }
            base.OnMouseMove(e);
        }
 
        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && _SourceTabPage != null)
            {
                TabPage currTabPage = GetTabPageFromXY(e.X, e.Y);
                if (currTabPage != null && !currTabPage.Equals(_SourceTabPage))
                {
                    if (TabPages.IndexOf(currTabPage) < TabPages.IndexOf(_SourceTabPage))
                    {
                        TabPages.Remove(_SourceTabPage);
                        TabPages.Insert(TabPages.IndexOf(currTabPage), _SourceTabPage);
                        SelectedTab = _SourceTabPage;
                    }
                    else if (TabPages.IndexOf(currTabPage) > TabPages.IndexOf(_SourceTabPage))
                    {
                        TabPages.Remove(_SourceTabPage);
                        TabPages.Insert(TabPages.IndexOf(currTabPage) + 1, _SourceTabPage);
                        SelectedTab = _SourceTabPage;
 
                    }
                }
            }
            _SourceTabPage = null;
            Cursor = Cursors.Default;
            base.OnMouseUp(e);
        }
 
        TabPage GetTabPageFromXY(int x, int y)
        {
            for (int i = 0; i < TabCount; i++)
                if (GetTabRect(i).Contains(x, y)) return TabPages[i];
            return null;
        }
    }
}

AnswerRe: CSharp versionmemberJosip Medved8 Jun '12 - 18:46 
Thanks for adding this code.
 
BTW. There is update to this article at http://www.jmedved.com/2009/06/reordering-tabpages-inside-tabcontrol/. I have my take on C# code there.
QuestionSmall QuestionmemberMember 79877066 Jun '12 - 10:52 
I'm very new to programming and I'm trying to implement this code. I understand what is going on, but I don't get where to put this code. Do I put it in the Form.Load sub? Any help is greatly appreciated. Thanks.
AnswerRe: Small QuestionmemberRonon4614 Jul '12 - 4:40 
download the source code and study it
GeneralVery helpful articlememberanna.novikova1 Jul '10 - 21:55 
Thanks for this article. I adapted this code to C# and it works great for me.
GeneralMy vote of 5memberanna.novikova1 Jul '10 - 21:53 
Very helpful article! Thanks!
GeneralNicememberDeath8U5 Feb '09 - 3:07 
I actually did this in a very similar manner. I agree, I like this method much better than drag n drop.
GeneralDndTabControl functionality.memberMijaeDjinn1 Jul '08 - 10:25 
I just did a cut-n-paste with the code into my project. Ran it, and it worked like a charm. This was just what I was looking for. Thanks.
 
I have all the answers to life, just ask the questions.

JokeRe: DndTabControl functionality.memberJosip Medved21 Jul '08 - 9:50 
I'm glad you found it useful.
 
--
Pozdrav,
Josip Medved
http://www.jmedved.com

GeneralDndTabControl functionality.memberpbnec3 Dec '08 - 21:35 
Is there any C# version of program code of this article?
Note that I have read the article "Drag and Drop Tab Control"~
http://codeproject.com/KB/tabs/draggabletabcontrol.aspx[^]
, but the coding is completely different. That article uses the events "MouseDown" and "DragOver" (although the event "MouseMove" should be also used), and this article ("Reordering TabPages inside TabControl") uses the events "MouseDown", "MouseMove" and "MouseUp".
AnswerRe: DndTabControl functionality.memberJosip Medved5 Dec '08 - 4:32 
Currently, there is no C# version, but you may try some VB.NET to C# web conversion tool. Code is quite simple, so there should not be any problems. In case that there are problems, just contact me again, I will write it.
Regarding events, we just took different approach to things. I like my version better of course. Smile | :)
 
--
Pozdrav,
Josip Medved
http://www.jmedved.com

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 12 Sep 2006
Article Copyright 2006 by Josip Medved
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid