TabControl
IE や Firefox のように、実行時に WinForms のタブを並べ替えることができますか?
このようなリンクはあまり期待できません。
もちろん可能です!ソリューションを過度に複雑にしようとしている可能性が最も高いです。基本的に、標準をサブクラス化TabControl
し、マウス イベント ハンドラにロジックを追加するだけです。ユーザーが現在ドラッグしているフォームを確認し、TabPages
コレクション内で並べ替えるだけです。
オンラインで入手できる完全なソリューションがいくつかあります。
@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 は、個別のアプリケーション間、または少なくとも個別のコントロール間でドラッグすることを目的としています。この場合にそれらを使用するのはやり過ぎです。
それは彼に基づいているので、私のものに賛成票を投じる場合は、コーディの答えにも賛成票を投じてください。
ドラッグ アンド ドロップによるタブページの並べ替え- http://dotnetrix.co.uk/tabcontrol.htm#tip7
に触発された Ludwig B.作
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();
}
ジェイコブ・スタンリーの答えを少し拡張しました。これにより、スワッピングが頻繁に発生することはありません。これは、異なるサイズのタブの場合に特に役立ちます。この場合、以前のソリューションではドラッグ中に頻繁に交換されていました。
ユーザー エクスペリエンスの違いは、実際にタブを移動するには、もう少しドラッグする必要があることです。しかし、これはブラウザでのタブの並べ替えに似ています。
また、ドラッグ中にハンドカーソルを追加し、ダブルバッファリングを有効にしました。
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;
}
}
}