Est-il possible de faire en sorte que WinForms Tab Control soit capable de réorganiser les tabs comme IE ou Firefox?

Est-il possible de réorganiser les tabs dans WinForms TabControl au moment de l’exécution, comme IE ou Firefox?

Des liens comme celui -ci ne me donnent pas beaucoup d’espoir.

Bien sûr, c’est possible! Vous essayez probablement de compliquer à l’excès la solution. En gros, tout ce que vous avez à faire est de sous- TabControl le TabControl standard et d’append une certaine logique aux gestionnaires d’événements de la souris. Vous aurez juste besoin de vérifier quel formulaire l’utilisateur est en train de faire glisser et de le réorganiser dans la collection TabPages .

Deux solutions complètes sont disponibles en ligne:

  • Réorganisation des tabs dans TabControl
  • Glisser et déposer le contrôle onglet
  • Repositionner TabItems à l’exécution

La solution initialement publiée par @Cody Grey était pour l’essentiel celle que je souhaitais, mais je ne voyais pas la nécessité de la compliquer autant.

Ceci est ma simplification, implémentée en dérivant de 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; } } 

Les API de glisser-déposer sont vraiment conçues pour faire glisser des éléments entre des applications distinctes ou, à tout le moins, des contrôles distincts. Les utiliser dans ce cas est excessif.

Assurez-vous également que vous augmentez la réponse de Cody si vous augmentez la mienne, car elle est basée sur la sienne.

réordonnancement de TabPages avec drag and dropde Ludwig B.
inspiré par http://dotnesortingx.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 ((eX - tcLocation.X) > ((hoverTabRect.X + hoverTabRect.Width) - dragTabRect.Width)) this.swapTabPages(tc, dragTab, hoverTab); } else if (dragTab_index > hoverTab_index) { if ((eX - 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(); } 

J’ai prolongé un peu la réponse de Jacob Stanley. De cette façon, la permutation ne se produira pas trop souvent. Ceci est particulièrement utile pour les tabs de différentes tailles, auquel cas la solution précédente permuterait très souvent en faisant glisser.

La différence dans l’expérience utilisateur est que vous devez faire glisser un peu plus loin pour déplacer l’onglet. Mais cela ressemble à la réorganisation des tabs dans les navigateurs.

Aussi, j’ai ajouté un curseur de main en faisant glisser et activé double tampon.

 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; } } }