Reordering TabPages inside TabControl






4.57/5 (12 votes)
Extending Microsoft TabControl to enable reordering of TabPages
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 TabPage
s 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 TabPage
s inside a Tab
. I think that the only thing one may hold against it is updating the order on Control
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