C# ListView間のマウスD&Dによる写真の移動とコピー

listView間の画像移動・コピー 

 C# listView関係のまとめとして、同じ listView内だけでなく、異なる listView間の画像移動・コピーが出来る様に検討してみました。下図はテストプログラム起動後、画像No〔5〕〔6〕〔8〕を選択した状態です。
 次の図は選択したlistView1の画像をlistView2に移動(Move)した状態です。 

 次の図は選択したlistView1の画像をlistView2にコピー(Copy)した状態です。

 移動・コピーは、listView2 から listView1 の反対方向も可能です。

プログラム

 プログラムとしては、過去投稿の応用です。悩んだところは、①移動・コピー時に元データが同じ、もしくは他方のlistViewなのか判定する方法と②全くなにもないlistViewに対し画像をドロップする時、“Insert”が使えなかったことです。
 ①に対してはプロパティ情報で取得でき、②はターゲットの listViewの項目数によって、“Add” と “Insert”を使い分ける様に変更しました。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.IO;

namespace ListViewTest
{
    public partial class Form4 : Form
    {
        private string img_dir;

        public Form4()
        {
            InitializeComponent();
        }

        private void Form4_Load(object sender, EventArgs e)
        {
            img_dir = @""; // 画像ディレクトリ

            listView1_init();
            get_pic_thumbnail(1);
            chk_listView1(0);
            chk_listView2(0);
        }

        private void listView1_init()
        {
            listView1.Items.Clear();
            listView2.Items.Clear();

            imageList1.Images.Clear();


            listView1.LargeImageList = imageList1;
            listView1.AllowDrop = true;
            listView1.MultiSelect = true;

            listView2.LargeImageList = imageList1;
            listView2.AllowDrop = true;
            listView2.MultiSelect = true;

            listView1.ItemDrag += new ItemDragEventHandler(listView1_ItemDrag);
            listView1.DragOver += new DragEventHandler(listView1_DragOver);
            listView1.DragDrop += new DragEventHandler(listView1_DragDrop);

            listView2.ItemDrag += new ItemDragEventHandler(listView2_ItemDrag);
            listView2.DragOver += new DragEventHandler(listView2_DragOver);
            listView2.DragDrop += new DragEventHandler(listView2_DragDrop);

        }


        // 1-①ドラッグ時1回発生するイベント
        private void listView1_ItemDrag(Object sender, ItemDragEventArgs e)
        {
            listView1.DoDragDrop((ListViewItem)e.Item, DragDropEffects.Copy | DragDropEffects.Move);
        }


        // 1-②アイテムドラッグ中に繰り返し発生するイベント
        private void listView1_DragOver(object sender, System.Windows.Forms.DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(ListViewItem)))
            {
                if ((e.KeyState & 0x1) > 0 && (e.KeyState & 0x8) > 0)
                {   
                    e.Effect = DragDropEffects.Copy;
                }
                else
                {
                    e.Effect = DragDropEffects.Move;
                }

                Point targetPoint = listView1.PointToClient(new Point(e.X, e.Y));

                int targetIndex = listView1.InsertionMark.NearestIndex(targetPoint);
                int ins_p_Index = targetIndex;                              // 中間位置

                // 最近アイテムが、現ドラッグ項目(自分自身)の場合は、-1 が返される。
                if (targetIndex > -1)
                {
                    Rectangle itemBounds = listView1.GetItemRect(targetIndex);
                    if (targetPoint.X > itemBounds.Left + (itemBounds.Width / 2))
                    {
                        listView1.InsertionMark.AppearsAfterItem = true;    // 近いアイテム後方
                        ins_p_Index++;                                      // 中間位置
                    }
                    else
                    {
                        listView1.InsertionMark.AppearsAfterItem = false;   // 近いアイテム前方
                    }
                }

                listView1.InsertionMark.Index = targetIndex;
            }
        }

        // 1-③ドロップ時1回発生するイベント
        private void listView1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(ListViewItem)))
            {
                Point targetPoint = listView1.PointToClient(new Point(e.X, e.Y));
                int targetIndex = listView1.InsertionMark.NearestIndex(targetPoint);                // 近い位置
                int ins_p_Index = targetIndex;                                                      // 中間位置

                ListViewItem srcItem = (ListViewItem)e.Data.GetData(typeof(ListViewItem));

                // 挿入先の画像アイテムがゼロの場合を考慮
                if (targetIndex > -1 || (targetIndex == -1 && listView1.Items.Count == 0))
                {
                    if (targetIndex > -1)
                    {   // マウスカーソルの右・左によって修正
                        Rectangle itemBounds = listView1.GetItemRect(targetIndex);
                        if (targetPoint.X > itemBounds.Left + (itemBounds.Width / 2))
                        {
                            ins_p_Index++;                                                              // 中間位置
                        }
                    }

                    int item_cnt = srcItem.ListView.SelectedItems.Count;                                   // 選択画像数

                    if (item_cnt > 0)
                    {
                        ListViewItem[] item_selected = new ListViewItem[srcItem.ListView.SelectedItems.Count];

                        // 選択項目を一旦配列に格納
                        for (int i = 0; i < srcItem.ListView.SelectedItems.Count; i++)
                        {   item_selected[i] = srcItem.ListView.SelectedItems[i]; }

                        // 配列に格納した選択項目を順番に追加
                        for (int i = 0; i < item_selected.Length; i++)
                        {
                            int exec_order = item_selected.Length - i - 1;          // 追加順番を逆転し挿入する

                            if (ins_p_Index == -1)
                            {
                                listView1.Items.Add(item_selected[exec_order].Text, item_selected[exec_order].ImageIndex);
                                ins_p_Index = 0;
                            }
                            else
                            {
                                listView1.Items.Insert(ins_p_Index, item_selected[exec_order].Text, item_selected[exec_order].ImageIndex);
                            }
                        }

                        // 移動(Move)時ソースアイテム削除
                        if ((e.KeyState & 0x8) <= 0)
                        {
                            for (int i = 0; i < item_selected.Length; i++)
                            {
                                if (item_selected[i] != null)
                                {
                                    item_selected[i].ListView.Items.Remove(item_selected[i]);
                                }
                            }
                        }
                    }

                    chk_listView1(1);                                                               // 再描画  
                    chk_listView2(1);
                }
            }
        }


        // 1-①ドラッグ時1回発生するイベント
        private void listView2_ItemDrag(Object sender, ItemDragEventArgs e)
        {
            listView2.DoDragDrop((ListViewItem)e.Item, DragDropEffects.Copy | DragDropEffects.Move);
        }

        // 2-②アイテムドラッグ中に繰り返し発生するイベント
        private void listView2_DragOver(object sender, System.Windows.Forms.DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(ListViewItem)))
            {
                if ((e.KeyState & 0x1) > 0 && (e.KeyState & 0x8) > 0)
                {   
                    e.Effect = DragDropEffects.Copy;
                }
                else
                {
                    e.Effect = DragDropEffects.Move;
                }

                Point targetPoint = listView2.PointToClient(new Point(e.X, e.Y));

                int targetIndex = listView2.InsertionMark.NearestIndex(targetPoint);
                int ins_p_Index = targetIndex;                              // 中間位置

                // 最近アイテムが、現ドラッグ項目(自分自身)の場合は、-1 が返される。
                if (targetIndex > -1)
                {
                    Rectangle itemBounds = listView2.GetItemRect(targetIndex);
                    if (targetPoint.X > itemBounds.Left + (itemBounds.Width / 2))
                    {
                        listView2.InsertionMark.AppearsAfterItem = true;    // 近いアイテム後方
                        ins_p_Index++;                                      // 中間位置
                    }
                    else
                    {
                        listView2.InsertionMark.AppearsAfterItem = false;   // 近いアイテム前方
                    }
                }

                listView2.InsertionMark.Index = targetIndex;
            }
        }

        // 2-③ドロップ時1回発生するイベント
        private void listView2_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(ListViewItem)))
            {
                Point targetPoint = listView2.PointToClient(new Point(e.X, e.Y));
                int targetIndex = listView2.InsertionMark.NearestIndex(targetPoint);                // 近い位置
                int ins_p_Index = targetIndex;                                                      // 中間位置

                ListViewItem srcItem = (ListViewItem)e.Data.GetData(typeof(ListViewItem));

                // 挿入先の画像アイテムがゼロの場合を考慮
                if (targetIndex > -1 || (targetIndex == -1 && listView2.Items.Count == 0))
                {
                    if (targetIndex > -1)
                    {   // マウスカーソルの右・左によって修正
                        Rectangle itemBounds = listView2.GetItemRect(targetIndex);
                        if (targetPoint.X > itemBounds.Left + (itemBounds.Width / 2)){ ins_p_Index++; }
                    }

                    //listBox2.Items.Clear();
                    int item_cnt = srcItem.ListView.SelectedItems.Count;                            // 選択画像数

                    if (item_cnt > 0)
                    {
                        ListViewItem[] item_selected = new ListViewItem[srcItem.ListView.SelectedItems.Count];

                        // 選択項目を一旦配列に格納
                        for (int i = 0; i < srcItem.ListView.SelectedItems.Count; i++)
                        {   item_selected[i] = srcItem.ListView.SelectedItems[i]; }


                        // 配列に格納した選択項目を順番に追加
                        for (int i = 0; i < item_selected.Length; i++)
                        {
                            int exec_order = item_selected.Length - i - 1;                          // 追加順番を逆転挿入

                            if (ins_p_Index == -1)
                            {
                                listView2.Items.Add(item_selected[exec_order].Text, item_selected[exec_order].ImageIndex);
                                ins_p_Index = 0 ;
                            }
                            else 
                            {
                                listView2.Items.Insert(ins_p_Index, item_selected[exec_order].Text, item_selected[exec_order].ImageIndex);
                            }
    
                        }

                        // 移動(Move)時ソースアイテム削除
                        if ((e.KeyState & 0x8) <= 0)
                        {
                            for (int i = 0; i < item_selected.Length; i++)
                            {
                                if (item_selected[i] != null)
                                {
                                    item_selected[i].ListView.Items.Remove(item_selected[i]);
                                }
                            }
                        }
                    }

                    chk_listView1(1);                                                               // 再描画  
                    chk_listView2(1);

                }
            }
        }


        // サムネイル画像取得・listView表示
        private void get_pic_thumbnail(int kbn)
        {
            string[] jpgFiles = System.IO.Directory.GetFiles(img_dir, "*.PNG");

            int width = 80;
            int height = 80;

            imageList1.ImageSize = new Size(width, height);

            // imageList , listView 登録
            for (int i = 0; i < jpgFiles.Length; i++)
            {
                String F_Name = Path.GetFileName(jpgFiles[i]);
                Image original = Bitmap.FromFile(jpgFiles[i]);
                Image thumbnail = createThumbnail(original, width, height);

                imageList1.Images.Add(F_Name, thumbnail);

                //imageList1.Images.Add(thumbnail);

                if (kbn == 1) { listView1.Items.Add(F_Name, i); }
                else if (kbn == 2) { listView2.Items.Add(F_Name, i); }
                else { 
                    listView1.Items.Add(F_Name, i);
                    listView2.Items.Add(F_Name, i);
                }          

                original.Dispose();
                thumbnail.Dispose();
            }
        }


        // 幅w、高さh サムネイル画像作成
        Image createThumbnail(Image image, int w, int h)
        {
            Bitmap canvas = new Bitmap(w, h);

            Graphics g = Graphics.FromImage(canvas);
            g.FillRectangle(new SolidBrush(Color.White), 0, 0, w, h);

            float fw = (float)w / (float)image.Width;
            float fh = (float)h / (float)image.Height;

            float scale = Math.Min(fw, fh);
            fw = image.Width * scale;
            fh = image.Height * scale;

            g.DrawImage(image, (w - fw) / 2, (h - fh) / 2, fw, fh);
            g.Dispose();

            return canvas;
        }


        // アイテムの関連プロパティ取得・再表示
        private void chk_listView1(int kbn)
        {
            //listBox1.Items.Clear();
            // リストビュー1(listView1)プロパティ
            int listViewCnt1 = listView1.Items.Count;
            string[,] dat_vw1 = new string[listViewCnt1, 4];
            for (int i = 0; i < listViewCnt1; i++)
            {
                dat_vw1[i, 0] = listView1.Items[i].Index.ToString();
                dat_vw1[i, 1] = listView1.Items[i].Text;
                dat_vw1[i, 2] = listView1.Items[i].ImageIndex.ToString();
                dat_vw1[i, 3] = listView1.Items[i].ImageKey;

                //listBox1.Items.Add(dat_vw1[i, 0] + "  ,  " + dat_vw1[i, 1] + "  ,  " + dat_vw1[i, 2] + "  ,  " + dat_vw1[i, 3]);
            }

            // listViewを一旦初期化し再表示する
            if (kbn == 1)
            {
                listView1.Clear();

                for (int i = 0; i < dat_vw1.GetLength(0); i++)
                {
                    listView1.Items.Add(dat_vw1[i, 1], int.Parse(dat_vw1[i, 2]));
                }
            }
        }

        // アイテムの関連プロパティ取得・再表示
        private void chk_listView2(int kbn)
        {
            //listBox1.Items.Clear();
            // リストビュー1(listView1)プロパティ
            int listViewCnt2 = listView2.Items.Count;
            string[,] dat_vw1 = new string[listViewCnt2, 4];
            for (int i = 0; i < listViewCnt2; i++)
            {
                dat_vw1[i, 0] = listView2.Items[i].Index.ToString();
                dat_vw1[i, 1] = listView2.Items[i].Text;
                dat_vw1[i, 2] = listView2.Items[i].ImageIndex.ToString();
                dat_vw1[i, 3] = listView2.Items[i].ImageKey;

                //listBox1.Items.Add(dat_vw1[i, 0] + "  ,  " + dat_vw1[i, 1] + "  ,  " + dat_vw1[i, 2] + "  ,  " + dat_vw1[i, 3]);
            }

            // listViewを一旦初期化し再表示する
            if (kbn == 1)
            {
                listView2.Clear();

                for (int i = 0; i < dat_vw1.GetLength(0); i++)
                {
                    listView2.Items.Add(dat_vw1[i, 1], int.Parse(dat_vw1[i, 2]));
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            listView1.Items.Clear();
            listView2.Items.Clear();
            imageList1.Images.Clear();
            get_pic_thumbnail(2);
            chk_listView1(0);
            chk_listView2(0);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            listView1.Items.Clear();
            listView2.Items.Clear();
            imageList1.Images.Clear();
            get_pic_thumbnail(1);
            chk_listView1(0);
            chk_listView2(0);
        }
    }
}

 

実際の写真での処理

 listView間の画像の移動・コピーは写真の編集(並び替え)を想定したものです。プログラム作成中は、確実に並び替えが出来ているか確認する目的で番号の書いてある画像を用いましたが、最後に実際に撮影した写真を使ってみます。サムネイル画像に変換する前は、約2~7Mbyte程度ある写真です。
 下図はプログラム起動後、3つ写真選択しています。


 選んだ3写真を移動しました。

 更に2写真をコピーしています。

 

まとめ

 とりあえず、listView を使って、やりたいと思っていたことは実現出来ました。次回以降は同じくC#で描画処理を検討していくつもりです。