 |
|
 |
it doesnt work
i cant even open the source demo.....
|
|
|
|
 |
|
 |
Hello,
is there a way to get a event, when reordering is ready?
I look for MouseUp but this is never fired!
cu
Carsten
|
|
|
|
 |
|
 |
Even after seven years of publication there are some weirdo's (=me) using your suggested component extension, Paul! Isn't that wonderfull?
I encountered a shortcomming when the tab sizes of the drag tab and the hover tab are different. Especially when the drag tab is smaller then the hover tab.
But with some small adaptation I managed to overcome this issue.
Hereby the code pieces which I modified:
(also most of the other enhancements from the previous posts to this article are already included in this code part)
The OnDragOver event:
protected override void OnDragOver(System.Windows.Forms.DragEventArgs e)
{
base.OnDragOver(e);
Point pt = new Point(e.X, e.Y);
pt = PointToClient(pt);
if (e.Data.GetDataPresent(typeof(TabPage)))
{
TabPage drag_tab = (TabPage)e.Data.GetData(typeof(TabPage));
TabPage hover_tab = GetTabPageByTab(pt, drag_tab); if (hover_tab != null)
{
e.Effect = DragDropEffects.Move;
int item_drag_index = FindIndex(drag_tab);
int drop_location_index = FindIndex(hover_tab);
if (item_drag_index != drop_location_index)
{
this.SuspendLayout();
TabPages[drop_location_index] = drag_tab;
TabPages[item_drag_index] = hover_tab;
SelectedTab = drag_tab;
this.ResumeLayout();
}
}
}
else
{
e.Effect = DragDropEffects.None;
}
}
The new (additional) GetTabPageByTab method:
private TabPage GetTabPageByTab(Point pt, TabPage dragTab)
{
TabPage tp = null;
Rectangle dragTabRect = GetTabRect(FindIndex(dragTab));
for (int i = 0; i < TabPages.Count; i++)
{
Rectangle hoverTabRect = GetTabRect(i);
if (dragTabRect.Width < hoverTabRect.Width)
{
if (dragTabRect.X < hoverTabRect.X)
hoverTabRect.X += hoverTabRect.Width - dragTabRect.Width;
else
hoverTabRect.Width -= hoverTabRect.Width - dragTabRect.Width;
}
if (hoverTabRect.Contains(pt))
{
tp = TabPages[i];
break;
}
}
return tp;
}
The only side effect of this implementation is that when hovering over a tab the switch of tabs is only done when the cursor comes out of the blind zone (blind zone equals the difference between tab sizes).
Happy Coding!
|
|
|
|
 |
|
 |
Awesome stuff -- this fixed the exact problem I was having with the original implementation. MUCH thanks!!
|
|
|
|
 |
|
 |
Hello Mr. Auger,
Could you please advise me on the following question.
What's the story with its reuse in commercial products, is it allowed?
Are there any restrictions, copyright statements to be inserted, credit given etc?
Regards,
2twotango
|
|
|
|
 |
|
 |
This took me about an hour to figure out. Otherwise the code works great.
Just remember that when you create a new instance of the "DraggableTabControl" that you need to set it's AllowDrop attribute to true.
Dim MyCT As New DraggableTabControl
MyCT.Location = New Point(390, 80)
MyCT.Name = "mtcSections"
MyCT.AllowDrop = True
Me.Controls.Add(MyCT)
Hope this wasn't obivious and I just made myself look stupid.
|
|
|
|
 |
|
 |
Tanx for the work, it does a good job. I've added some code in case you have tablabels with different widths. Having a small beside a big one gives increadeble flickering so I added some lines:
Just put it under:
// No need to replace the tab by itself
if (item_drag_index == drop_location_index)
return;
----------new begin
Rectangle selRect = GetTabRect(item_drag_index);
Rectangle dropRect = GetTabRect(drop_location_index);
if (selRect.Top == dropRect.Top )
if (item_drag_index < drop_location_index)
{
if (pt.X < (selRect.Left + dropRect.Width))
return;
}
else
if (pt.X > (dropRect.Left + dropRect.Width))
return;
----------new end
and:
this.SuspendLayout();
Regards,
Henk
|
|
|
|
 |
|
|
 |
|
 |
Yes it works between tabcontrols.
modified on Monday, May 19, 2008 9:24 AM
|
|
|
|
 |
|
 |
i've been trying to do something similar,it tried to use GetChildAtPoint() to get the tab but it didn't work
finally i used ur code and it works nicely
so nice work man
|
|
|
|
 |
|
 |
Nice work Paul.
Below is the code in VB including Jeff Key's Non-flicker Code and Big Man's Start in MouseMove.
Thanks guys.
Public Class DraggableTabControl
Private mRectDragBoxFromMouseDown As Rectangle
Private mIsDragging As Boolean = False
Private mDragOffset As Point
'''
''' Gets the TabPage Rectangle under the mouse pointer.
'''
'''
''' Rectangle of the relevant TabPage.
'''
Public ReadOnly Property MouseDownRectangle() As Rectangle
Get
Return mRectDragBoxFromMouseDown
End Get
End Property
Protected Overrides Sub OnDragOver(ByVal e As System.Windows.Forms.DragEventArgs)
MyBase.OnDragOver(e)
Dim pt As Point = New Point(e.X, e.Y)
'We need client coordinates.
pt = PointToClient(pt)
'Get the tab we are hovering over.
Dim hoverTab As TabPage = GetTabPageByTab(pt)
'Make sure we are on a tab.
If hoverTab IsNot Nothing Then
'Make sure there is a TabPage being dragged.
If e.Data.GetDataPresent(GetType(TabPage)) Then
e.Effect = DragDropEffects.Move
Dim dragTab As TabPage = CType(e.Data.GetData(GetType(TabPage)), TabPage)
Dim itemDragIndex As Integer = FindIndex(dragTab)
Dim dropLocationIndex As Integer = FindIndex(hoverTab)
'Don't do anything if we are hovering over ourself.
If itemDragIndex <> dropLocationIndex Then
Dim selTabPage As TabPage = TabPages(itemDragIndex)
Dim repTabPage As TabPage = TabPages(dropLocationIndex)
TabPages(dropLocationIndex) = selTabPage
TabPages(itemDragIndex) = repTabPage
SelectedTab = selTabPage
End If
End If
Else
e.Effect = DragDropEffects.None
End If
End Sub
'''
''' Sets a member variable mRectDragBoxFromMouseDown to store the point where the mouse down event occured.
'''
''' Mouse X coordinate.
''' Mouse Y coordinate.
'''
Protected Sub CalcRectDragBox(ByVal x As Integer, ByVal y As Integer)
' Remember the point where the mouse down occurred. The DragSize indicates
' the size that the mouse can move before a drag event should be started.
Dim dragSize As Size = SystemInformation.DragSize
' Create a rectangle using the DragSize, with the mouse position being
' at the center of the rectangle.
mRectDragBoxFromMouseDown = New Rectangle(New _
Point(CInt(x - (dragSize.Width / 2)), CInt(y - (dragSize.Height / 2))), dragSize)
End Sub
Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
MyBase.OnMouseDown(e)
'Store the rectangle of the TabPage under the mouse.
CalcRectDragBox(e.X, e.Y)
End Sub
Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
MyBase.OnMouseMove(e)
' Handle Mouse move only if left button is pressed.
If e.Button = Windows.Forms.MouseButtons.Left Then
' If the mouse moves outside the rectangle, start the drag.
If mRectDragBoxFromMouseDown <> Rectangle.Empty And Not mRectDragBoxFromMouseDown.Contains(e.X, e.Y) Then
mIsDragging = True
mDragOffset = New Point(e.X, e.Y)
Invalidate()
' Proceed with the drag and drop.
Dim dropEffect As DragDropEffects = DoDragDrop(Me.SelectedTab, DragDropEffects.Move)
' Reset the drag box to avoid reentry of drag.
CalcRectDragBox(e.X, e.Y)
mIsDragging = False
mDragOffset = Point.Empty
Invalidate()
End If
End If
End Sub
'
' Finds the TabPage which contains the passed point.
'
' The point (given in client coordinates) to look for a TabPage.
' The TabPage whose tab is at the given point (null if there isn't one).
Friend Function GetTabPageByTab(ByVal pt As Point) As TabPage
Dim tp As TabPage = Nothing
Dim i As Integer = 0
Do While i < TabPages.Count
If GetTabRect(i).Contains(pt) Then
tp = TabPages(i)
End If
i += 1
Loop
Return tp
End Function
'
' Find the index of the given TabPage.
'
' The TabPage we want the index for.
' The index of the given TabPage(-1 if it isn't found.)
Private Function FindIndex(ByVal page As TabPage) As Integer
Dim i As Integer = 0
Do While i < TabPages.Count
If TabPages(i).Equals(page) Then
Return i
End If
i += 1
Loop
Return -1
End Function
End Class
Grant.
|
|
|
|
 |
|
 |
Hi, I noticed that when you drag a tab onto somewhere outside the form (in my case, the windows taskbar), there will be a COM exception exposed indicating wrong handle.
Is there anyone have idea about this?
|
|
|
|
 |
|
 |
I think this may have something to do with the way you are using the control. There is no COM used at all. When I compile it and run it and drag/drop a tab to the task bar - or to anywhere for that matter - I don't have any exceptions at all. I will be happy to help you track it down if you could get me a sample app that has the problem.
Sorry I wasn't more help.
PJA
|
|
|
|
 |
|
 |
Hi Paul,
Thanks for your quick response.
Actually, the problem I mentioned only occur in debug mode. And I have found the solution to that.
My way to do this is put DoDragDrop in event handler OnMouseMove instead of OnMouseDown. And override OnQueryContinueDrag method to judge whether the mouse pointer has gone to some invalid area. This is what MSDN told me and I'm using VS2005.
But anyway, your work provided a very good framework and starting point for me to study. Thanks again.
Eliot
|
|
|
|
 |
|
 |
//Make sure we are on a tab.
if(hover_tab != null)
{
//Make sure there is a TabPage being dragged.
if(e.Data.GetDataPresent(typeof(TabPage)))
{
e.Effect = DragDropEffects.Move;
TabPage drag_tab = (TabPage)e.Data.GetData(typeof(TabPage));
int item_drag_index = FindIndex(drag_tab);
int drop_location_index = FindIndex(hover_tab);
// No need to replace the tab by itself
if (item_drag_index == drop_location_index) return;
this.SuspendLayout ();
TabPage selTabPage = TabPages[item_drag_index];
TabPage repTabPage = TabPages[drop_location_index];
TabPages[drop_location_index] = selTabPage;
TabPages[item_drag_index] = repTabPage;
SelectedTab = selTabPage;
this.ResumeLayout ();
}
}
else
{
e.Effect = DragDropEffects.None;
}
|
|
|
|
 |
|
 |
SUper better !
the non-flickering solution is more logic and works better.
hundreds thanks.:->
[Others should opt for that alternative solution.]
|
|
|
|
 |
|
 |
It doesn't work if the mouse wonders off of the tab control. However I made a little adjustment to the code and it seems to work great. Probably could be optimized a little but here it is anyways.
Replace:
TabPage selTabPage = TabPages[item_drag_index];
TabPage repTabPage = TabPages[drop_location_index];
TabPages[drop_location_index] = selTabPage;
TabPages[item_drag_index] = repTabPage;
SelectedTab = selTabPage;
With:
bool isleft;
if (drop_location_index < item_drag_index)
isleft = true;
else
isleft = false;
this.SuspendLayout();
if (isleft)
{
Queue pages = new Queue();
pages.Enqueue(page);
for (int i = drop_location_index; i < TabPages.Count; i++)
{
if (TabPages[i] != page)
pages.Enqueue(TabPages[i]);
}
for (int i = drop_location_index; i < TabPages.Count; i++)
{
TabPages[i] = pages.Dequeue();
}
SelectedTab = page;
}
else
{
Queue pages = new Queue();
pages.Enqueue(page);
for (int i = drop_location_index; i > -1; i--)
{
if (TabPages[i] != page)
pages.Enqueue(TabPages[i]);
}
for (int i = drop_location_index; i > -1; i--)
{
TabPages[i] = pages.Dequeue();
}
SelectedTab = page;
}
|
|
|
|
 |
|
 |
Here is the new code:
using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms; namespace DraggableTabControl { [ToolboxBitmap(typeof(DraggableTabControl))] /// <summary> /// Summary description for DraggableTabPage. /// </summary> public class DraggableTabControl : System.Windows.Forms.TabControl { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; public DraggableTabControl() { // This call is required by the Windows.Forms Form Designer. InitializeComponent(); AllowDrop = true; // TODO: Add any initialization after the InitForm call } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { } #endregion private Rectangle m_rectDragBoxFromMouseDown; private Point m_dragOffset; protected override void OnDragOver(System.Windows.Forms.DragEventArgs e) { base.OnDragOver(e); Point pt = new Point(e.X, e.Y); //We need client coordinates. pt = PointToClient(pt); //Get the tab we are hovering over. TabPage hover_tab = GetTabPageByTab(pt); //Make sure we are on a tab. if(hover_tab != null) { //Make sure there is a TabPage being dragged. if(e.Data.GetDataPresent(typeof(TabPage))) { e.Effect = DragDropEffects.Move; TabPage drag_tab = (TabPage)e.Data.GetData(typeof(TabPage)); int item_drag_index = FindIndex(drag_tab); int drop_location_index = FindIndex(hover_tab); TabPage selTabPage = TabPages[item_drag_index]; TabPage repTabPage = TabPages[drop_location_index]; TabPages[drop_location_index] = selTabPage; TabPages[item_drag_index] = repTabPage; SelectedTab = selTabPage; } } else { e.Effect = DragDropEffects.None; } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove (e); // Handle Mouse move only if left button is pressed if ( ( e.Button & MouseButtons.Left ) == MouseButtons.Left ) { // If the mouse moves outside the rectangle, start the drag. if ( m_rectDragBoxFromMouseDown != Rectangle.Empty && !m_rectDragBoxFromMouseDown.Contains( e.X, e.Y ) ) { m_dragOffset = new Point( e.X, e.Y ) ; Invalidate() ; Point pt = new Point(e.X, e.Y); TabPage tp = GetTabPageByTab(pt); // Proceed with the drag and drop DragDropEffects dropEffect = DoDragDrop( tp, DragDropEffects.Move ) ; // Reset the drag box to avoid reentry of drag CalcRectDragBox( e.X, e.Y ) ; m_dragOffset = Point.Empty ; Invalidate() ; } } } protected void CalcRectDragBox( int x, int y ) { // Remember the point where the mouse down occurred. The DragSize indicates // the size that the mouse can move before a drag event should be started. Size dragSize = SystemInformation.DragSize ; // Create a rectangle using the DragSize, with the mouse position being // at the center of the rectangle. m_rectDragBoxFromMouseDown = new Rectangle( new Point( x - ( dragSize.Width /2 ), y - ( dragSize.Height /2 ) ), dragSize ) ; } protected override void OnMouseDown( MouseEventArgs e ) { base.OnMouseDown( e ) ; CalcRectDragBox( e.X, e.Y ) ; } /// <summary> /// Finds the TabPage whose tab is contains the given point. /// </summary> /// <param name="pt">The point (given in client coordinates) to look for a TabPage.</param> /// <returns>The TabPage whose tab is at the given point (null if there isn't one).</returns> private TabPage GetTabPageByTab(Point pt) { TabPage tp = null; for(int i = 0; i < TabPages.Count; i++) { if(GetTabRect(i).Contains(pt)) { tp = TabPages[i]; break; } } return tp; } /// <summary> /// Loops over all the TabPages to find the index of the given TabPage. /// </summary> /// <param name="page">The TabPage we want the index for.</param> /// <returns>The index of the given TabPage(-1 if it isn't found.)</returns> private int FindIndex(TabPage page) { for(int i = 0; i < TabPages.Count; i++) { if(TabPages[i] == page) return i; } return -1; } } }
|
|
|
|
 |
|
 |
Hello,
I want to copy tabpage and all the controls of tabpage to another tabcontrol. Is it possible?
If yes, can you please guide us how do i do that?
thank you in advance,
DNN
|
|
|
|
 |
|
 |
I am not sure whether you mean in the designer or programatically, so I will address both questions...
If you are in the designer, just right-click on the tab page to be copied and click "Copy" from the menu.
Right click on the new target tab control and click "Paste" from the menu.
Note that to select a page, you must first click the tab, then the designer area.
If you want to do it programmatically, this will work...
<code>TabPage tp = tabControl1.TabPages[0]; //Pick the real tab page here
tabControl1.TabPages.Remove(tp);
tabControl2.TabPages.Add(tp);</code>
Hope this helps.
Paul
|
|
|
|
 |
|
 |
I guess the example is rather more moving then copying...
I still haven't found out how to copy a TabPage into the SAME TabControl. I had hoped that there would be some kind of copy constructor, but I guess that is old fashioned C++.
Any suggestions would be great.
|
|
|
|
 |
|
 |
Removing all of the tab pages and re-adding them causes my pages to flicker like crazy. You don't need to do all that. Simply replace the following:
TabPages.Remove(selTabPage);
TabPages[drop_location_index] = selTabPage;
ArrayList pages = new ArrayList();
//Put all tab pages into an array.
for(int i = 0; i < TabPages.Count; i++)
{
//Except the one we are dragging.
if(i != item_drag_index)
pages.Add(TabPages[i]);
}
//Now put the one we are dragging it at the proper location.
pages.Insert(drop_location_index, drag_tab);
//Make them all go away for a nanosec.
TabPages.Clear();
//Add them all back in.
TabPages.AddRange((TabPage[])pages.ToArray(typeof(TabPage)));
//Make sure the drag tab is selected.
SelectedTab = drag_tab;
With:
TabPage selTabPage = TabPages[item_drag_index];
TabPage repTabPage = TabPages[drop_location_index];
TabPages[drop_location_index] = selTabPage;
TabPages[item_drag_index] = repTabPage;
SelectedTab = selTabPage;
-jk
|
|
|
|
 |
|
 |
Thank you. I experience flicker often as well. I guess the simplest solution usually is the correct one. I appreciate your input and I will incorporate this change quickly.
-Paul
|
|
|
|
 |
|
 |
Nice job, however the Drag & Drop should start from the MouseMove handler only if the mouse is out of a rectangle defined in the MouseDown handler. Currently Drag & Drop starts every time a tab page is clicked... Big Man
For example: protected void CalcRectDragBox( int x, int y ) { // Remember the point where the mouse down occurred. The DragSize indicates // the size that the mouse can move before a drag event should be started. Size dragSize = SystemInformation.DragSize ; // Create a rectangle using the DragSize, with the mouse position being // at the center of the rectangle. m_rectDragBoxFromMouseDown = new Rectangle( new Point( x - ( dragSize.Width /2 ), y - ( dragSize.Height /2 ) ), dragSize ) ; } protected override void OnMouseDown( MouseEventArgs e ) { base.OnMouseDown( e ) ; CalcRectDragBox( e.X, e.Y ) ; } protected override void OnMouseMove( MouseEventArgs e ) { base.OnMouseMove( e ) ; // Handle Mouse move only if left button is pressed if ( ( e.Button & MouseButtons.Left ) == MouseButtons.Left ) { // If the mouse moves outside the rectangle, start the drag. if ( m_rectDragBoxFromMouseDown != Rectangle.Empty && !m_rectDragBoxFromMouseDown.Contains( e.X, e.Y ) ) { m_isDragging = true ; m_dragOffset = new Point( e.X, e.Y ) ; Invalidate() ; // Proceed with the drag and drop DragDropEffects dropEffect = DoDragDrop( <YOUR OBJECT>, DragDropEffects.Move ) ; // Reset the drag box to avoid reentry of drag CalcRectDragBox( e.X, e.Y ) ; m_isDragging = false ; m_dragOffset = Point.Empty ; Invalidate() ; } } }
|
|
|
|
 |
|
 |
Note that this change also fixes the problem of the DraggableTabControl swallowing the DoubleClick event.
|
|
|
|
 |