C#リストビューのアイテムをマウスで移動する。

コンピュータ

リストビューのアイテムをマウスで移動する方法を調べてみました。

サンプルソース

using System;
using System.Windows.Forms;
using System.Drawing;
// 
// リストビューのアイテムをマウスで移動する。
// 
class Form1 : Form
{
    ListView listView;

    Form1()
    {
        listView = new ListView()
        {
            Dock = DockStyle.Fill,
            View = View.List,   // 表示...一覧
            AllowDrop = true,   // ドロップ...有効
            MultiSelect = false,    // 複数選択...無効
        };

        listView.ItemDrag += (sender, e) => {
            // アイテムがドラックされた。

            // リストビューのドラグアンドドロップを呼び出し
            listView.DoDragDrop(
                (ListViewItem)e.Item,
                DragDropEffects.Move
            );
        };

        listView.DragEnter += (sender, e) => {
            // ドラック開始

            // イベントオブジェクトがListViewItemで無い場合リターン
            if(e.Data.GetDataPresent(typeof(ListViewItem)) == false)
            {
                return;
            }

            // カーソルを移動表示に
            e.Effect = DragDropEffects.Move;
        };

        listView.DragDrop += (sender, e) => {
            // ドラックアンドドロップ

            // イベントオブジェクトがListViewItemで無い場合リターン
            if(e.Data.GetDataPresent(typeof(ListViewItem)) == false)
            {
                return;
            }

            // ドラックされたリストビューアイテムを取得
            ListViewItem dragItem = (ListViewItem)e.Data.GetData(typeof(ListViewItem));
            // イベントが発生した位置座標をリストビューのクライアント座標に変換
            Point p = listView.PointToClient(new Point(e.X, e.Y));
            // 位置座標からリストビューアイテムを取得
            ListViewItem currentItem = this.listView.GetItemAt(p.X, p.Y);
            // リストビューアイテムからインデックスを取得
            int currentIndex = this.listView.Items.IndexOf(currentItem);
            
            // インデックスが負の場合リターン
            if (currentIndex < 0)
            {
                return;
            }

            // 移動先がドラックアイテムより下の場合、移動先の後に挿入
            // そうでない場合は移動先の前に挿入
            if (currentIndex > dragItem.Index)
            {
                currentIndex++;
            }
            
            // リストビューアイテムの挿入
            ListViewItem newItem = this.listView.Items.Insert(currentIndex, dragItem.Text);
            // リストビューアイテムの選択
            newItem.Selected = true;
            // ドラックアイテムを削除
            listView.Items.Remove(dragItem);
        };

        this.Controls.Add(listView);

        listView.Columns.Add("名前");

        for(int i=0;i < 10;i++)
        {
            var lvi = new ListViewItem(String.Format("{0}番目",i+1), i);
            listView.Items.Add(lvi);
        }

    }
    [STAThread]
    static void Main()
    {
        Application.Run(new Form1());
    }
}

実行

6番目をマウスで選択

選択したままマウスを上にドラックしドロップ

アイテムが移動しました。

感想

調べる前はリストビューのプロパティを有効化するぐらいで実現できると思っていましたが、しっかりコードを書かなくてはいけないようです。
リストビュー固有の部分はItemDragイベントで、こちらでドラックアンドドロップイベントを発生させます。
後はドラックアンドドロップイベントで処理するのですが、イベントではドラックアンドドロップされたオブジェクトがリストビューのアイテムか確認しアイテム以外は処理しないようにしています。ファイルの一覧を扱う場合は、この判定を拡張しエクスプローラーからファイルが渡された場合などに対応させると実用的になりそうです。

ドラックされたアイテムの位置座標を取得し、その位置をもとにアイテムの挿入と削除をすることで移動させています。

今回は単行でのプログラムですが、将来的に複数行に対応する移動も考えてみたいと思います。

コメント