[C#]TabControl のタブをドラッグアンドドロップで入れ替えできるようにする

観測気球

収集物の記録書庫 a data archive of collection -- collectible toys

[要旨] 標準の .NET Framework の TabControl では、ドラッグアンドドロップによるタブの入れ替えがサポートされていないので、CodeProject で公開されているソースを参考に、拡張 TabControl を作ってみました。
[キーワード] TabControl,タブ,入れ替え

« 今週のお買い物 (2009.02.01~02.07) | トップページ | 02月14日のココロ日記(BlogPet) »

2009.02.11

[C#]TabControl のタブをドラッグアンドドロップで入れ替えできるようにする

C# というか .NET Framework の TabControl は、標準では、タブのドラッグアンドドロップによる入れ替えは実装されていません。派手なアニメーション付きでなくてもいいから、とりあえず、タブを入れ替えるようにしたい、という要求、需要はあると思います。

CodeProject: Drag and Drop Tab Control. Free source code and programming help」の DraggableTabControl コントロールを使えば、望みがかなって、タブの入れ替えができるようになります。ただ、この DraggableTabControl コントロールは、OnMouseDown() で DoDragDrop() しているため、単なるクリック(右クリックも含む)やダブルクリックが効かなくなってしまいます。さすがに、これはあんまりなので、DoDragDrop() するのを OnMouseMove() で行なうように改造してみました。

以下、そのソースです。コントロールの名前は TabControlEx に変えました。私が追加したメソッドは OnMouseMove(), OnMouseUp(), OnMouseLeave(), ClearDragTarget(), SetupDragTarget() で、あと OnMouseDown() を書き換えた以外には、プロパティを3つ追加したのと、コンストラクタを少しいじっています。

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;

namespace TabControlEx
{
    public class TabControlEx : TabControl
    {
        private int mouseDownPointX = 0;
        private int mouseDownPointY = 0;
        private Rectangle dragBoxFromMouseDown = Rectangle.Empty;
        
        public TabControlEx()
        {
            AllowDrop = true;
            ClearDragTarget();
        }

        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 OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (dragBoxFromMouseDown != Rectangle.Empty &&
                !dragBoxFromMouseDown.Contains(e.X, e.Y))
            {
                if (this.TabCount <= 1)
                    return;

                Point pt = new Point(mouseDownPointX, mouseDownPointY);
                TabPage tp = GetTabPageByTab(pt);

                if (tp != null)
                {
                    DoDragDrop(tp, DragDropEffects.All);
                }
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            ClearDragTarget();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            ClearDragTarget();
        }

        private void ClearDragTarget()
        {
            dragBoxFromMouseDown = Rectangle.Empty;
            mouseDownPointX = 0;
            mouseDownPointY = 0;
        }

        private void SetupDragTarget(int x, int y)
        {
            Size dragSize = SystemInformation.DragSize;

            dragBoxFromMouseDown =
                new Rectangle(new Point(x - (dragSize.Width / 2),
                                        y - (dragSize.Height / 2)), dragSize);
            mouseDownPointX = x;
            mouseDownPointY = y;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            ClearDragTarget();

            if (e.Button != MouseButtons.Left ||
                e.Clicks >= 2)
                return;

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

上記の TabControlEx を、従来の TabControl の代わりに使えば、ドラッグアンドドロップでタブが入れ替えられるようになります。

投稿者: tsupo 2009.02.11 午後 11:25 | 固定リンク | このエントリーをはてなブックマークに追加 | このエントリを del.icio.us に登録 このエントリの del.icio.us での登録状況 | このエントリを Buzzurl に追加このエントリの Buzzurl での登録状況 | このエントリをlivedoorクリップに登録 このエントリのlivedoorクリップでの登録状況 このエントリをlivedoorクリップに登録している人の数 | 酢鶏巡回中

楽天市場


品揃え豊富で安い!NTT-X Store


アマゾンわくわく探検隊

トラックバック

この記事のトラックバックURL:

この記事へのトラックバック一覧です: [C#]TabControl のタブをドラッグアンドドロップで入れ替えできるようにする:

コメント

ワード

ニッセン

fujisan.co.jp

楽天市場