note
Where The Sorting Hat's tab dragging comes from
The Sorting Hat lets you grab a tab and drag it to a new spot, and the tabs just... reorder themselves. It feels like a small thing until you've tried to write it yourself. It isn't mine. It's Mick Doherty's, and I've been carrying his code from project to project for years without ever saying so out loud. Fixing that.
dotnetrix.co.uk
Doherty's site, dotnetrix.co.uk, has been online since the early VB4 days — one of those sites that predates the idea of a "blog" and just calls itself what it is: tips and tricks for .NET Windows Forms, in VB.NET and C# side by side. By his own account, the TabControl page is the one people showed up for most — owner-drawn tabs, custom painting, context menus on tab headers, and the one I actually used: dragging tabs around.
TabDragger
The tip is titled, plainly, "A more complex TabDragging example" — tip #8 on his TabControl page. Drop his TabDragger class into a project, point it at your TabControl, and you get two behaviors for free: drag a tab onto another tab and they swap places (TabDragArrange), or drag a tab out of the strip entirely and it pops into its own small floating window (TabDragOut) that re-docks the moment you drag it back over the tab strip.
The reorder half works by hooking MouseDown and MouseMove on the control and asking Windows itself which tab is under the cursor, rather than guessing from coordinates:
private TabPage TabUnderMouse()
{
NativeMethods.TCHITTESTINFO HTI = new NativeMethods.TCHITTESTINFO(tabControl.PointToClient(Cursor.Position));
int tabID = NativeMethods.SendMessage(tabControl.Handle, NativeMethods.TCM_HITTEST, IntPtr.Zero, ref HTI);
return tabID == -1 ? null : tabControl.TabPages[tabID];
}
TCM_HITTEST is the native tab-control message for exactly this question — "which tab, if any, is at this point" — sent straight through SendMessage. From there, dragging one tab across another just swaps their positions in TabPages one step at a time until the dragged tab lands where the cursor is. The drag-out mode is the more involved half: it builds a borderless TabForm, hands it the tab's controls, and tracks WM_MOVING to decide whether you're still inside the tab strip (in which case it snaps back and re-docks) or out in free space (in which case it just floats).
The Sorting Hat uses the simple TabDragArrange mode, wired into its own tab control with the file renamed into the app's own namespace — otherwise the code is his, basically untouched. No drag-out, no floating windows, just "pick up a tab, drop it somewhere else, done."
Credit where it's due: dotnetrix.co.uk, and specifically the TabControl tips page, tip #8. Thanks, Mick.
← back to notes