是否可以像IE或Firefox一样在运行时对WinForms TabControl中的选项卡进行重新排序?Links like this不要给予我太多希望。
TabControl
tf7tbtn21#
通过拖放重新排序TabPages- * 作者:Ludwig B.*
灵感来自http://dotnetrix.co.uk/tabcontrol.htm#tip7
private void tc_MouseDown(object sender, MouseEventArgs e) { // store clicked tab TabControl tc = (TabControl)sender; int hover_index = this.getHoverTabIndex(tc); if (hover_index >= 0) { tc.Tag = tc.TabPages[hover_index]; } } private void tc_MouseUp(object sender, MouseEventArgs e) { // clear stored tab TabControl tc = (TabControl)sender; tc.Tag = null; } private void tc_MouseMove(object sender, MouseEventArgs e) { // mouse button down? tab was clicked? TabControl tc = (TabControl)sender; if ((e.Button != MouseButtons.Left) || (tc.Tag == null)) return; TabPage clickedTab = (TabPage)tc.Tag; int clicked_index = tc.TabPages.IndexOf(clickedTab); // start drag n drop tc.DoDragDrop(clickedTab, DragDropEffects.All); } private void tc_DragOver(object sender, DragEventArgs e) { TabControl tc = (TabControl)sender; // a tab is draged? if (e.Data.GetData(typeof(TabPage)) == null) return; TabPage dragTab = (TabPage)e.Data.GetData(typeof(TabPage)); int dragTab_index = tc.TabPages.IndexOf(dragTab); // hover over a tab? int hoverTab_index = this.getHoverTabIndex(tc); if (hoverTab_index < 0) { e.Effect = DragDropEffects.None; return; } TabPage hoverTab = tc.TabPages[hoverTab_index]; e.Effect = DragDropEffects.Move; // start of drag? if (dragTab == hoverTab) return; // swap dragTab & hoverTab - avoids toggeling Rectangle dragTabRect = tc.GetTabRect(dragTab_index); Rectangle hoverTabRect = tc.GetTabRect(hoverTab_index); if (dragTabRect.Width < hoverTabRect.Width) { Point tcLocation = tc.PointToScreen(tc.Location); if (dragTab_index < hoverTab_index) { if ((e.X - tcLocation.X) > ((hoverTabRect.X + hoverTabRect.Width) - dragTabRect.Width)) this.swapTabPages(tc, dragTab, hoverTab); } else if (dragTab_index > hoverTab_index) { if ((e.X - tcLocation.X) < (hoverTabRect.X + dragTabRect.Width)) this.swapTabPages(tc, dragTab, hoverTab); } } else this.swapTabPages(tc, dragTab, hoverTab); // select new pos of dragTab tc.SelectedIndex = tc.TabPages.IndexOf(dragTab); } private int getHoverTabIndex(TabControl tc) { for (int i = 0; i < tc.TabPages.Count; i++) { if (tc.GetTabRect(i).Contains(tc.PointToClient(Cursor.Position))) return i; } return -1; } private void swapTabPages(TabControl tc, TabPage src, TabPage dst) { int index_src = tc.TabPages.IndexOf(src); int index_dst = tc.TabPages.IndexOf(dst); tc.TabPages[index_dst] = src; tc.TabPages[index_src] = dst; tc.Refresh(); }
blpfk2vs2#
我把雅各布·斯坦利的回答延伸了一点。这样交换就不会太频繁。这对于不同大小的选项卡特别有用,在这种情况下,以前的解决方案在拖动时会经常交换。用户体验的不同之处在于,您必须再拖动一点才能实际移动选项卡。但这类似于浏览器中的标签重新排序。此外,我添加了一个手光标,而拖动和启用双缓冲。
using System; using System.Drawing; using System.Windows.Forms; namespace Controls { public class DraggableTabControl : TabControl { private TabPage draggedTab; public DraggableTabControl() { this.MouseDown += OnMouseDown; this.MouseMove += OnMouseMove; this.Leave += new System.EventHandler(this.DraggableTabControl_Leave); this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); } private void OnMouseDown(object sender, MouseEventArgs e) { draggedTab = TabAt(e.Location); } private void OnMouseMove(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Left || draggedTab == null) { this.Cursor = this.DefaultCursor; draggedTab = null; return; } int index = TabPages.IndexOf(draggedTab); int nextIndex = index + 1; int prevIndex = index - 1; int minXForNext = int.MaxValue; int maxXForPrev = int.MinValue; var tabRect = GetTabRect(index); if (nextIndex < TabPages.Count) { var nextTabRect = GetTabRect(nextIndex); minXForNext = tabRect.Left + nextTabRect.Width; } if (prevIndex >= 0) { var prevTabRect = GetTabRect(prevIndex); maxXForPrev = prevTabRect.Left + tabRect.Width; } this.Cursor = Cursors.Hand; if (e.Location.X > maxXForPrev && e.Location.X < minXForNext) { return; } TabPage tab = TabAt(e.Location); if (tab == null || tab == draggedTab) { return; } Swap(draggedTab, tab); SelectedTab = draggedTab; } private TabPage TabAt(Point position) { int count = TabCount; for (int i = 0; i < count; i++) { if (GetTabRect(i).Contains(position)) { return TabPages[i]; } } return null; } private void Swap(TabPage a, TabPage b) { int i = TabPages.IndexOf(a); int j = TabPages.IndexOf(b); TabPages[i] = b; TabPages[j] = a; } private void DraggableTabControl_Leave(object sender, EventArgs e) { this.Cursor = this.DefaultCursor; draggedTab = null; } } }
2cmtqfgy3#
这是一个非常古老的问题,但如果有人要使用它。功能中建议的解决方案存在问题,该功能会交换选项卡。TabPages和Controls集合不同步,交换后TabPages.RemoveAt的行为将不正确。以下是修改它的方法:
tc.TabPages[index_src] = dst; tc.Controls.SetChildIndex(tc.Controls[index_src], index_dst); tc.Refresh();
c6ubokkw4#
**当然,这是可能的!**你很可能试图把解决方案复杂化。本质上,您所要做的就是继承标准TabControl并向鼠标事件处理程序添加一些逻辑。您只需要检查用户当前正在拖动的表单,并在TabPages集合中对其进行重新排序。
TabPages
在线提供了几个完整的解决方案:
xe55xuns5#
我发现最初由@Cody Gray发布的解决方案主要是我想要的,但我不认为它需要如此复杂。这是我的简化,通过从TabControl派生实现:
public class DraggableTabControl : TabControl { private TabPage m_DraggedTab; public DraggableTabControl() { MouseDown += OnMouseDown; MouseMove += OnMouseMove; } private void OnMouseDown(object sender, MouseEventArgs e) { m_DraggedTab = TabAt(e.Location); } private void OnMouseMove(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Left || m_DraggedTab == null) { return; } TabPage tab = TabAt(e.Location); if (tab == null || tab == m_DraggedTab) { return; } Swap(m_DraggedTab, tab); SelectedTab = m_DraggedTab; } private TabPage TabAt(Point position) { int count = TabCount; for (int i = 0; i < count; i++) { if (GetTabRect(i).Contains(position)) { return TabPages[i]; } } return null; } private void Swap(TabPage a, TabPage b) { int i = TabPages.IndexOf(a); int j = TabPages.IndexOf(b); TabPages[i] = b; TabPages[j] = a; } }
拖放API实际上是用于在独立的应用程序之间拖动内容,或者至少是在独立的控件之间拖动内容。在这种情况下使用它们是过度的。如果你赞成我的答案,一定要赞成科迪的答案,因为它是基于他的。
5条答案
按热度按时间tf7tbtn21#
通过拖放重新排序TabPages- * 作者:Ludwig B.*
灵感来自http://dotnetrix.co.uk/tabcontrol.htm#tip7
blpfk2vs2#
我把雅各布·斯坦利的回答延伸了一点。这样交换就不会太频繁。这对于不同大小的选项卡特别有用,在这种情况下,以前的解决方案在拖动时会经常交换。
用户体验的不同之处在于,您必须再拖动一点才能实际移动选项卡。但这类似于浏览器中的标签重新排序。
此外,我添加了一个手光标,而拖动和启用双缓冲。
2cmtqfgy3#
这是一个非常古老的问题,但如果有人要使用它。功能中建议的解决方案存在问题,该功能会交换选项卡。TabPages和Controls集合不同步,交换后TabPages.RemoveAt的行为将不正确。以下是修改它的方法:
c6ubokkw4#
**当然,这是可能的!**你很可能试图把解决方案复杂化。本质上,您所要做的就是继承标准
TabControl
并向鼠标事件处理程序添加一些逻辑。您只需要检查用户当前正在拖动的表单,并在TabPages
集合中对其进行重新排序。在线提供了几个完整的解决方案:
xe55xuns5#
我发现最初由@Cody Gray发布的解决方案主要是我想要的,但我不认为它需要如此复杂。
这是我的简化,通过从TabControl派生实现:
拖放API实际上是用于在独立的应用程序之间拖动内容,或者至少是在独立的控件之间拖动内容。在这种情况下使用它们是过度的。
如果你赞成我的答案,一定要赞成科迪的答案,因为它是基于他的。