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 }












0 comments:
Post a Comment