Add the following classes to your project and make one of the following calls to your forms constructor after the InitializeComponent() call (assumes your TabControl is named TabControl1):
This will give you simple DragArrange of the tabs.
[VB] Dim DragTabs As new TabDragger(TabControl1)
[C#] TabDragger DragTabs = New TabDragger(this.tabControl1);
...and this will give you the ability to drag tabs out of the tabcontrol.
[VB] Dim DragTabs As New TabDragger(TabControl1, TabDragBehavior.TabDragout)
[C#] TabDragger DragTabs = new TabDragger(this.tabControl1, TabDragBehavior.TabDragOut);
This will give you simple DragArrange of the tabs.
[VB] Dim DragTabs As new TabDragger(TabControl1)
[C#] TabDragger DragTabs = New TabDragger(this.tabControl1);
...and this will give you the ability to drag tabs out of the tabcontrol.
[VB] Dim DragTabs As New TabDragger(TabControl1, TabDragBehavior.TabDragout)
[C#] TabDragger DragTabs = new TabDragger(this.tabControl1, TabDragBehavior.TabDragOut);
VB net Class:
Friend Class TabDragger Public Sub New(ByVal tabControl As TabControl) MyBase.New() Me.tabControl = tabControl AddHandler tabControl.MouseDown, AddressOf tabControl_MouseDown AddHandler tabControl.MouseMove, AddressOf tabControl_MouseMove AddHandler tabControl.DoubleClick, AddressOf tabControl_DoubleClick End Sub Public Sub New(ByVal tabControl As TabControl, ByVal behavior As TabDragBehavior) Me.New(tabControl) Me._dragBehavior = behavior End Sub Private tabControl As TabControl Private dragTab As TabPage = Nothing Private _dragBehavior As TabDragBehavior = TabDragBehavior.TabDragArrange Private ReadOnly Property DragBehavior() As TabDragBehavior Get If (Not tabControl.Multiline) Then Return _dragBehavior End If Return TabDragBehavior.None End Get End Property Private Sub tabControl_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) dragTab = TabUnderMouse() End Sub Private Sub tabControl_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) If (_dragBehavior = TabDragBehavior.None) Then Return End If If (e.Button = MouseButtons.Left) Then If (dragTab IsNot Nothing) Then If (tabControl.TabPages.Contains(dragTab)) Then If (PointInTabStrip(e.Location)) Then Dim hotTab As TabPage = TabUnderMouse() If (hotTab IsNot dragTab AndAlso hotTab IsNot Nothing) Then Dim id1 As Int32 = tabControl.TabPages.IndexOf(dragTab) Dim id2 As Int32 = tabControl.TabPages.IndexOf(hotTab) If (id1 > id2) Then For id As Int32 = id2 To id1 SwapTabPages(id1, id) Next Else For id As Int32 = id2 To id1 Step -1 SwapTabPages(id1, id) Next End If tabControl.SelectedTab = dragTab End If Else If (Me._dragBehavior = TabDragBehavior.TabDragOut) Then If (dragTab.Tag IsNot Nothing) Then DirectCast(dragTab.Tag, TabForm).Dispose() dragTab.Tag = Nothing Else Dim frm As New TabForm(dragTab) End If End If End If End If End If End If End Sub Private Sub tabControl_DoubleClick(ByVal sender As Object, ByVal e As EventArgs) If (Me._dragBehavior = TabDragBehavior.TabDragOut) Then Dim frm As New TabForm(dragTab) End If End Sub #Region " Private Methods " Private Function TabUnderMouse() As TabPage Dim HTI As NativeMethods.TCHITTESTINFO = New NativeMethods.TCHITTESTINFO(tabControl.PointToClient(Cursor.Position)) Dim tabID As Int32 = NativeMethods.SendMessage(tabControl.Handle, NativeMethods.TCM_HITTEST, IntPtr.Zero, HTI) Return If(tabID = -1, Nothing, tabControl.TabPages(tabID)) End Function Private Function PointInTabStrip(ByVal point As Point) As Boolean Dim tabBounds As Rectangle = Rectangle.Empty Dim displayRC As Rectangle = tabControl.DisplayRectangle Select Case tabControl.Alignment Case TabAlignment.Bottom tabBounds.Location = New Point(0, displayRC.Bottom) tabBounds.Size = New Size(tabControl.Width, tabControl.Height - displayRC.Height) Case TabAlignment.Left tabBounds.Size = New Size(displayRC.Left, tabControl.Height) Case TabAlignment.Right tabBounds.Location = New Point(displayRC.Right, 0) tabBounds.Size = New Size(tabControl.Width - displayRC.Width, tabControl.Height) Case Else tabBounds.Size = New Size(tabControl.Width, displayRC.Top) End Select tabBounds.Inflate(-3, -3) Return tabBounds.Contains(point) End Function Private Sub SwapTabPages(ByVal index1 As Int32, ByVal index2 As Int32) If ((index1 Or index2) <> -1) Then Dim tab1 As TabPage = tabControl.TabPages(index1) Dim tab2 As TabPage = tabControl.TabPages(index2) tabControl.TabPages(index1) = tab2 tabControl.TabPages(index2) = tab1 End If End Sub #End Region End Class Friend Class TabForm Inherits Form Public Sub New(ByVal tabPage As TabPage) MyBase.New() Me.FormBorderStyle = FormBorderStyle.FixedToolWindow Me.StartPosition = FormStartPosition.Manual Me.MinimizeBox = False Me.MaximizeBox = False Me.tabPage = tabPage tabPage.Tag = Me Me.tabControl = DirectCast(tabPage.Parent, TabControl) Me.tabID = tabControl.TabPages.IndexOf(tabPage) Me.ClientSize = tabPage.Size Me.Location = tabControl.PointToScreen(New Point(tabPage.Left, tabControl.PointToClient(Cursor.Position).Y - SystemInformation.ToolWindowCaptionHeight \ 2)) Me.Text = tabPage.Text UnDockFromTab() Me.dragOffset = tabControl.PointToScreen(Cursor.Position) Me.dragOffset.X -= Me.Location.X Me.dragOffset.Y -= Me.Location.Y End Sub Private tabPage As TabPage Private tabControl As TabControl Private tabID As Int32 Private dragOffset As Point Protected Overrides Sub OnClosed(ByVal e As EventArgs) MyBase.OnClosed(e) DockToTab() End Sub Protected Overrides Sub WndProc(ByRef m As Message) If (m.Msg = NativeMethods.WM_MOVING) Then Dim rc As NativeMethods.RECT = DirectCast(m.GetLParam(GetType(NativeMethods.RECT)), NativeMethods.RECT) Dim pt As Point = tabControl.PointToClient(Cursor.Position) Dim pageRect As Rectangle = tabControl.DisplayRectangle Dim tabsRect As Rectangle = Rectangle.Empty Select Case tabControl.Alignment Case TabAlignment.Left tabsRect.Size = New Size(pageRect.Left, tabControl.Height) Case TabAlignment.Bottom tabsRect.Location = New Point(0, pageRect.Bottom) tabsRect.Size = New Size(tabControl.Width, tabControl.Bottom - pageRect.Bottom) Case TabAlignment.Right tabsRect.Location = New Point(pageRect.Right, 0) tabsRect.Size = New Size(tabControl.Right - pageRect.Right, tabControl.Height) Case Else tabsRect.Size = New Size(tabControl.Width, pageRect.Top) End Select If tabsRect.Contains(pt) Then DockToTab() Else UnDockFromTab() End If End If MyBase.WndProc(m) Select Case m.Msg Case NativeMethods.WM_NCLBUTTONDBLCLK Me.Close() Case NativeMethods.WM_EXITSIZEMOVE If (Not Me.Visible) Then Me.Close() End If Case NativeMethods.WM_MOUSEMOVE If (m.WParam.ToInt32() = 1) Then If (Not captured) Then Dim pt As Point = tabControl.PointToScreen((Cursor.Position)) Dim newPosition As Point = New Point(pt.X - dragOffset.X, pt.Y - dragOffset.Y) Me.Location = newPosition End If Dim rc As NativeMethods.RECT = New NativeMethods.RECT(Me.Bounds) Dim lParam As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(rc)) Marshal.StructureToPtr(rc, lParam, True) NativeMethods.SendMessage(Me.Handle, NativeMethods.WM_MOVING, IntPtr.Zero, lParam) Marshal.FreeHGlobal(lParam) End If Case NativeMethods.WM_SETCURSOR captured = True Case Else End Select End Sub Private captured As Boolean Private Sub DockToTab() If (Not tabControl.TabPages.Contains(tabPage)) Then For id As Int32 = Me.Controls.Count - 1 To 0 Step -1 tabPage.Controls.Add(Me.Controls(0)) Next tabControl.TabPages.Insert(tabID, tabPage) tabControl.SelectedTab = tabPage tabControl.Capture = True Me.Close() End If End Sub Private Sub UnDockFromTab() If (Me.Visible OrElse Me.IsDisposed) Then Return End If For id As Int32 = tabPage.Controls.Count - 1 To 0 Step -1 Me.Controls.Add(tabPage.Controls(0)) Next tabControl.TabPages.Remove(tabPage) Me.Capture = True Me.Show() End Sub End Class Friend NotInheritable Class NativeMethods <StructLayout(LayoutKind.Sequential)> _ Public Structure RECT Public Left, Top, Right, Bottom As Int32 Public Sub New(ByVal bounds As Rectangle) Me.Left = bounds.Left Me.Top = bounds.Top Me.Right = bounds.Right Me.Bottom = bounds.Bottom End Sub Public Overrides Function ToString() As String Return String.Format("{0}, {1}, {2}, {3}", Left, Top, Right, Bottom) End Function End Structure Public Const WM_NCLBUTTONDBLCLK As Int32 = &HA3 Public Const WM_SETCURSOR As Int32 = &H20 Public Const WM_NCHITTEST As Int32 = &H84 Public Const WM_MOUSEMOVE As Int32 = &H200 Public Const WM_MOVING As Int32 = &H216 Public Const WM_EXITSIZEMOVE As Int32 = &H232 <DllImport("user32.dll")> _ Public Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Int32, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Int32 End Function <DllImport("user32.dll")> _ Public Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal msg As Int32, ByVal wParam As IntPtr, ByRef lParam As TCHITTESTINFO) As Int32 End Function <StructLayout(LayoutKind.Sequential)> _ Public Structure TCHITTESTINFO Public pt As Point Public flags As TCHITTESTFLAGS Public Sub New(ByVal point As Point) pt = point flags = TCHITTESTFLAGS.TCHT_ONITEM End Sub End Structure <Flags()> _ Public Enum TCHITTESTFLAGS TCHT_NOWHERE = 1 TCHT_ONITEMICON = 2 TCHT_ONITEMLABEL = 4 TCHT_ONITEM = TCHT_ONITEMICON Or TCHT_ONITEMLABEL End Enum Public Const TCM_HITTEST As Int32 = &H130D End Class Public Enum TabDragBehavior None TabDragArrange TabDragOut End Enum
CSharp Class:
internal class TabDragger { public TabDragger(TabControl tabControl) : base() { this.tabControl = tabControl; tabControl.MouseDown +=new MouseEventHandler(tabControl_MouseDown); tabControl.MouseMove += new MouseEventHandler(tabControl_MouseMove); tabControl.DoubleClick += new EventHandler(tabControl_DoubleClick); } public TabDragger(TabControl tabControl, TabDragBehavior behavior) : this(tabControl) { this.dragBehavior = behavior; } private TabControl tabControl; private TabPage dragTab = null; private TabDragBehavior dragBehavior = TabDragBehavior.TabDragArrange; private TabDragBehavior DragBehavior { get { if (!tabControl.Multiline) return dragBehavior; return TabDragBehavior.None; } } private void tabControl_MouseDown(object sender, MouseEventArgs e) { dragTab = TabUnderMouse(); } private void tabControl_MouseMove(object sender, MouseEventArgs e) { if (DragBehavior == TabDragBehavior.None) return; if (e.Button == MouseButtons.Left) { if (dragTab != null) { if (tabControl.TabPages.Contains(dragTab)) { if (PointInTabStrip(e.Location)) { TabPage hotTab = TabUnderMouse(); if (hotTab != dragTab && hotTab != null) { int id1 = tabControl.TabPages.IndexOf(dragTab); int id2 = tabControl.TabPages.IndexOf(hotTab); if (id1 > id2) { for (int id = id2; id <= id1; id++) { SwapTabPages(id1, id); } } else { for (int id = id2; id > id1; id--) { SwapTabPages(id1, id); } } tabControl.SelectedTab = dragTab; } } else { if (this.dragBehavior == TabDragBehavior.TabDragOut) { if (dragTab.Tag != null) { ((TabForm)dragTab.Tag).Dispose(); dragTab.Tag = null; } else { TabForm frm = new TabForm(dragTab); } } } } } } } private void tabControl_DoubleClick(object sender, EventArgs e) { if (this.DragBehavior == TabDragBehavior.TabDragOut) { TabForm frm = new TabForm(dragTab); } } #region Private Methods 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]; } private bool PointInTabStrip(Point point) { Rectangle tabBounds = Rectangle.Empty; Rectangle displayRC = tabControl.DisplayRectangle; ; switch (tabControl.Alignment) { case TabAlignment.Bottom: tabBounds.Location = new Point(0, displayRC.Bottom); tabBounds.Size = new Size(tabControl.Width, tabControl.Height - displayRC.Height); break; case TabAlignment.Left: tabBounds.Size = new Size(displayRC.Left, tabControl.Height); break; case TabAlignment.Right: tabBounds.Location = new Point(displayRC.Right, 0); tabBounds.Size = new Size(tabControl.Width - displayRC.Width, tabControl.Height); break; default: tabBounds.Size = new Size(tabControl.Width, displayRC.Top); break; } tabBounds.Inflate(-3, -3); return tabBounds.Contains(point); } private void SwapTabPages(int index1, int index2) { if ((index1 | index2) != -1) { TabPage tab1 = tabControl.TabPages[index1]; TabPage tab2 = tabControl.TabPages[index2]; tabControl.TabPages[index1] = tab2; tabControl.TabPages[index2] = tab1; } } #endregion } internal class TabForm : Form { public TabForm(TabPage tabPage) : base() { this.FormBorderStyle = FormBorderStyle.FixedToolWindow; this.StartPosition = FormStartPosition.Manual; this.MinimizeBox = false; this.MaximizeBox = false; this.tabPage = tabPage; tabPage.Tag = this; this.tabControl = (TabControl)tabPage.Parent; this.tabID = tabControl.TabPages.IndexOf(tabPage); this.ClientSize = tabPage.Size; this.Location = tabControl.PointToScreen(new Point(tabPage.Left, tabControl.PointToClient(Cursor.Position).Y - SystemInformation.ToolWindowCaptionHeight / 2)); this.Text = tabPage.Text; UnDockFromTab(); this.dragOffset = tabControl.PointToScreen(Cursor.Position); this.dragOffset.X -= this.Location.X ; this.dragOffset.Y -= this.Location.Y; } private TabPage tabPage; private TabControl tabControl; private int tabID; private Point dragOffset; protected override void OnClosed(EventArgs e) { base.OnClosed(e); DockToTab(); } protected override void WndProc(ref Message m) { if (m.Msg == NativeMethods.WM_MOVING) { NativeMethods.RECT rc = (NativeMethods.RECT)m.GetLParam(typeof(NativeMethods.RECT)); Point pt = tabControl.PointToClient(Cursor.Position); Rectangle pageRect = tabControl.DisplayRectangle; Rectangle tabsRect = Rectangle.Empty; switch (tabControl.Alignment) { case TabAlignment.Left: tabsRect.Size = new Size(pageRect.Left, tabControl.Height); break; case TabAlignment.Bottom: tabsRect.Location = new Point(0, pageRect.Bottom); tabsRect.Size = new Size(tabControl.Width, tabControl.Bottom - pageRect.Bottom); break; case TabAlignment.Right: tabsRect.Location = new Point(pageRect.Right, 0); tabsRect.Size = new Size(tabControl.Right - pageRect.Right, tabControl.Height); break; default: tabsRect.Size = new Size(tabControl.Width, pageRect.Top); break; } if (tabsRect.Contains(pt)) DockToTab(); else UnDockFromTab(); } base.WndProc(ref m); switch (m.Msg) { case NativeMethods.WM_NCLBUTTONDBLCLK: this.Close(); break; case NativeMethods.WM_EXITSIZEMOVE: if (!this.Visible) this.Close(); break; case NativeMethods.WM_MOUSEMOVE: if (m.WParam.ToInt32() == 1) { if (!captured) { Point pt = tabControl.PointToScreen((Cursor.Position)); Point newPosition = new Point(pt.X - dragOffset.X, pt.Y - dragOffset.Y); this.Location = newPosition; } NativeMethods.RECT rc = new NativeMethods.RECT(this.Bounds); IntPtr lParam = Marshal.AllocHGlobal(Marshal.SizeOf(rc)); Marshal.StructureToPtr(rc, lParam, true); NativeMethods.SendMessage(this.Handle, NativeMethods.WM_MOVING, IntPtr.Zero, lParam); Marshal.FreeHGlobal(lParam); } break; case NativeMethods.WM_SETCURSOR: captured = true; break; default: break; } } private bool captured; private void DockToTab() { if (!tabControl.TabPages.Contains(tabPage)) { for (int id = this.Controls.Count - 1; id >= 0; id--) { tabPage.Controls.Add(this.Controls[0]); } tabControl.TabPages.Insert(tabID, tabPage); tabControl.SelectedTab = tabPage; tabControl.Capture = true; this.Close(); } } private void UnDockFromTab() { if (this.Visible || this.IsDisposed) return; for (int id = tabPage.Controls.Count - 1; id >= 0; id--) { this.Controls.Add(tabPage.Controls[0]); } tabControl.TabPages.Remove(tabPage); this.Capture = true; this.Show(); } } internal sealed class NativeMethods { [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; public RECT(Rectangle bounds) { this.Left = bounds.Left; this.Top = bounds.Top; this.Right = bounds.Right; this.Bottom = bounds.Bottom; } public override string ToString() { return String.Format("{0}, {1}, {2}, {3}", Left, Top, Right, Bottom); } } public const int WM_NCLBUTTONDBLCLK = 0xA3; public const int WM_SETCURSOR = 0x20; public const int WM_NCHITTEST = 0x84; public const int WM_MOUSEMOVE = 0x200; public const int WM_MOVING = 0x216; public const int WM_EXITSIZEMOVE = 0x232; [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wParam, ref TCHITTESTINFO lParam); [StructLayout(LayoutKind.Sequential)] public struct TCHITTESTINFO { public Point pt; public TCHITTESTFLAGS flags; public TCHITTESTINFO(Point point) { pt = point; flags = TCHITTESTFLAGS.TCHT_ONITEM; } } [Flags()] public enum TCHITTESTFLAGS { TCHT_NOWHERE = 1, TCHT_ONITEMICON = 2, TCHT_ONITEMLABEL = 4, TCHT_ONITEM = TCHT_ONITEMICON | TCHT_ONITEMLABEL } public const int TCM_HITTEST = 0x130D; } public enum TabDragBehavior {None, TabDragArrange, TabDragOut }
No comments:
Post a Comment