可拖放的选项卡控件






4.70/5 (18投票s)
2002年6月16日
2分钟阅读

273370

5856
通过拖放来重新排序选项卡控件中的TabPage。
引言
由于我的最终用户需要能够通过拖放功能重新排序一组选项卡,我开始寻找具有这种行为的控件。事实上,我自己也经常在Visual Studio.NET IDE中使用此功能来更好地管理我的文档。由于此功能没有“内置”到.NET TabControl
中,我便着手从头开始创建该控件(或者至少扩展TabControl
)。这项努力的最终成果是DraggableTabControl
。
在从TabControl
派生类之后,我发现唯一棘手的部分是在DragOver
事件中确定鼠标悬停在哪个TabPage
上。当用户拖动TabControl
的实际选项卡时,TabControl
本身实际上会获得该事件,而不是TabPage
。为了克服这个问题,我编写了一个小函数来确定鼠标被拖动的点是否包含在每个选项卡的矩形内。请参阅下面的函数FindTabPageByTab
。
private TabPage GetTabPageByTab(Point pt) { TabPage tp = null; //Loop over the tab pages, using GetTabRect() to determine //if that pages Tab contains the point we passed in. for(int i = 0; i < TabPages.Count; i++) { if(GetTabRect(i).Contains(pt)) { //We found the tab page, no need to go on... tp = TabPages[i]; break; } } return tp; }
为了完成TabPage
的重新排序,我首先将除正在拖动的选项卡之外的所有选项卡放入一个数组中,然后在正确的位置将正在拖动的选项卡插入到数组中。然后我清除所有TabPages
并将我的数组添加回TabControl
。但是,重要的是,我们应该尽可能少地执行此操作,以避免闪烁等问题。因此,代码中包含许多检查。请参阅下面的代码片段,看看我在说什么...
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); //Don't do anything if we are hovering over ourself. if(item_drag_index != drop_location_index) { 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; } } } else { e.Effect = DragDropEffects.None; } }
这个类相当简单明了,应该可以完全继承和委托。要使用它,您可以将其构建成自己的程序集,或者将其添加到您可能已经拥有的控件库中。您可以将它添加为现有解决方案中的项目,或者简单地将.cs文件添加到现有项目中。请注意,如果您只是将.cs文件添加到项目中,还需要将 DraggableTabControl.bmp文件添加到项目中,或者删除 [ToolboxBitmap]
属性。如果您将其构建成自己的程序集或与现有的控件库一起编译,可以通过右键单击它并选择“自定义工具箱...”然后在“.NET 组件”选项卡下浏览到它来将其添加到工具箱中。
除此之外,注释代码几乎不言自明。我已完整包含 DraggableTabControl
类源代码如下。
using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms; namespace DraggableTabControl { [ToolboxBitmap(typeof(DraggableTabControl))] /// /// Summary description for DraggableTabPage. /// public class DraggableTabControl : System.Windows.Forms.TabControl { /// /// Required designer variable. /// private System.ComponentModel.Container components = null; public DraggableTabControl() { // This call is required by the Windows.Forms Form Designer. InitializeComponent(); // TODO: Add any initialization after the InitForm call } /// /// Clean up any resources being used. /// protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { } #endregion 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); //Don't do anything if we are hovering over ourself. if(item_drag_index ! = drop_location_index) { 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; } } } else { e.Effect = DragDropEffects.None; } } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); Point pt = new Point(e.X, e.Y); TabPage tp = GetTabPageByTab(pt); if(tp != null) { DoDragDrop(tp, DragDropEffects.All); } } /// /// Finds the TabPage whose tab is contains the given 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). 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; } /// /// Loops over all the TabPages to 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 int FindIndex(TabPage page) { for(int i = 0; i < TabPages.Count; i++) { if(TabPages[i] == page) return i; } return -1; } } }