出会いセンター ドラッグ中に半透明のイメージを表示させる方法(C#)

ドラッグ中に半透明のイメージを表示させる方法

エクスプローラなどのアプリケーションではファイルをドラッグするときに半透明のイメージが一緒に ドラッグされるようになっています。このTipsは、この半透明のイメージを表示させる方法についてです。

半透明なWindowを利用する方法

Formにはその不透明度を設定するOpacityというプロパティがあります。これを利用すれば、所望の ことは容易にできると思われます。

確かに可能ですが、ドラッグ中の小さなイメージごときにFormを使うのは勿体無い気がしますし、 最前面に表示するためにTopMostを設定したり、タスクバーに表示させないようにShowInTaskbar を設定したりと、色々面倒です。更にこのFormを表示したときにはフォーカスも移ってしまうので、 その対策も必要です。簡単にできそうですが、何かと不便なところもありそうです。

Win32 APIに用意されている機能を使う方法

やはりこのような機能はWin32 APIに予め用意されているものです。使用するAPIは以下の5つです。

この他、ImageList_DragShowNolockなどのAPIもありますが、ここでは割愛します。

さて、これらのAPIは名前をみたらその使い方が想像できるようなものばかりです。実際その通りで容易に 所望のことを実現できます。しかし、ひとつ注意することがあります。それは、これらのAPIに渡す 座標です。ImageList_BeginDrag、ImageList_DragMove、ImageList_DragEnter、にはそれぞれ指定された 形式の座標を渡してやる必要があります。

まず、ImageList_BeginDragはドラッグ中に表示されるイメージにおける相対座標を指定します。 つまり、ListBoxの場合GetItemRectangleを使ってドラッグしたいアイテムのRectangleを取得し、 現在あるポインタ座標からその値を引いたものが、イメージの左上からの相対座標になるということです。

一方ImageList_DragMove、ImageList_DragEnterに指定する座標はWindowの左上からの相対座標を渡す必要が あります、ここで注意しなければならないのは、Window全体における相対座標であって、クライアント領域 における相対座標ではないということです。従って、PointToClientは使うことができないということです。 実際には、現在のポインタ座標から、WindowのLeftとTopを引いて算出します。

これらのことにさえ注意すれば、難なく使えると思われます。次にその使用例を示します。 二つのListBox間でテキストのドラッグ&ドロップを行うサンプルです。

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

namespace DragGhostImageSample
{
    /// <summary>
    /// Form1
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
        private System.Windows.Forms.ListBox lstSource;
        private System.Windows.Forms.ListBox lstDest;
        private System.ComponentModel.IContainer components;
        private System.Windows.Forms.ImageList imageList1;

        // MouseDown時の座標保存変数
        private Point mouseDownPoint = Point.Empty;

        #region コンストラクタ

        public Form1()
        {
            InitializeComponent();
            for(int i=0; i<10; i++)
                lstSource.Items.Add("item" + i.ToString());
        }

        #endregion

        #region Dispose処理

        /// <summary>
        /// 使用されているリソースに後処理を実行します。
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #endregion

        #region Windows フォーム デザイナで生成されたコード
        /// <summary>
        /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディタで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.lstSource = new System.Windows.Forms.ListBox();
            this.lstDest = new System.Windows.Forms.ListBox();
            this.imageList1 = new System.Windows.Forms.ImageList(this.components);
            this.SuspendLayout();
            //
            // lstSource
            //
            this.lstSource.AllowDrop = true;
            this.lstSource.ItemHeight = 12;
            this.lstSource.Location = new System.Drawing.Point(0, 8);
            this.lstSource.Name = "lstSource";
            this.lstSource.Size = new System.Drawing.Size(120, 160);
            this.lstSource.TabIndex = 0;
            this.lstSource.MouseDown += new System.Windows.Forms.MouseEventHandler(this.lstSource_MouseDown);
            this.lstSource.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.lstSource_QueryContinueDrag);
            this.lstSource.MouseUp += new System.Windows.Forms.MouseEventHandler(this.lstSource_MouseUp);
            this.lstSource.MouseMove += new System.Windows.Forms.MouseEventHandler(this.lstSource_MouseMove);
            this.lstSource.DragEnter += new System.Windows.Forms.DragEventHandler(this.lstSource_DragEnter);
            this.lstSource.GiveFeedback += new System.Windows.Forms.GiveFeedbackEventHandler(this.lstSource_GiveFeedback);
            //
            // lstDest
            //
            this.lstDest.AllowDrop = true;
            this.lstDest.ItemHeight = 12;
            this.lstDest.Location = new System.Drawing.Point(152, 8);
            this.lstDest.Name = "lstDest";
            this.lstDest.Size = new System.Drawing.Size(120, 160);
            this.lstDest.TabIndex = 1;
            this.lstDest.DragDrop += new System.Windows.Forms.DragEventHandler(this.lstDest_DragDrop);
            this.lstDest.DragEnter += new System.Windows.Forms.DragEventHandler(this.lstDest_DragEnter);
            //
            // imageList1
            //
            this.imageList1.ImageSize = new System.Drawing.Size(16, 16);
            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
            //
            // Form1
            //
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
            this.ClientSize = new System.Drawing.Size(272, 173);
            this.Controls.Add(this.lstDest);
            this.Controls.Add(this.lstSource);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }
        #endregion

        #region メインエントリポイント

        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }

        #endregion


        #region lstSource側の処理

        private void lstSource_MouseDown(object sender,
                                        System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                if (((ListBox)sender).IndexFromPoint(e.X, e.Y) >= 0)
                    mouseDownPoint = new Point(e.X, e.Y);
            else
                mouseDownPoint = Point.Empty;
        }

        private void lstSource_MouseMove(object sender,
                                        System.Windows.Forms.MouseEventArgs e)
        {
            if (mouseDownPoint != Point.Empty)
            {
                Rectangle dragRegion = new Rectangle(
                    mouseDownPoint.X - SystemInformation.DragSize.Width / 2,
                    mouseDownPoint.Y - SystemInformation.DragSize.Height / 2,
                    SystemInformation.DragSize.Width,
                    SystemInformation.DragSize.Height);
                if (!dragRegion.Contains(e.X, e.Y))
                {
                    // ListBoxからドラッグ中のアイテムを取得
                    ListBox lst = (ListBox)sender;
                    int itemIndex = lst.IndexFromPoint(mouseDownPoint);
                    if (itemIndex < 0) return;
                    string itemText = (string)lst.Items[itemIndex];

                    // Imageの初期化
                    imageList1.Images.Clear();
                    Rectangle itemRect = lst.GetItemRectangle(itemIndex);
                    imageList1.ImageSize = new Size(itemRect.Width, itemRect.Height);
            
                    // 半透明イメージの元画像を作成、ImageListに追加
                    Bitmap bmp = new Bitmap(itemRect.Width, itemRect.Height);
                    Graphics g = Graphics.FromImage(bmp);
                    g.DrawString(itemText, lst.Font, new SolidBrush(lst.ForeColor), 0, 0);
                    imageList1.Images.Add(bmp);

                    // ImageList_BeginDragにはドラッグする
                    // イメージの中における相対座標を指定する

                    if(DragImage.ImageList_BeginDrag(imageList1.Handle, 0,
                                                     e.X - itemRect.Left,
                                                     e.Y - itemRect.Top))
                    {
                        lstSource.DoDragDrop(itemText, DragDropEffects.Copy);
                        DragImage.ImageList_EndDrag();
                    }
                    mouseDownPoint = Point.Empty;
                }
            }
        }

        private void lstSource_MouseUp(object sender,
                                       System.Windows.Forms.MouseEventArgs e)
        {
            mouseDownPoint = Point.Empty;        
        }

        private void lstSource_DragEnter(object sender,
                                         System.Windows.Forms.DragEventArgs e)
        {
            // ImageList_DragEnterにはクライアント領域における相対座標ではなく
            // タイトルバーなどの非クライアント領域を含むWindowにおける相対座標を指定する
            Point p = this.PointToClient(Cursor.Position);
            int x = Cursor.Position.X - this.Left;
            int y = Cursor.Position.Y - this.Top;
            // ドラッグ中は半透明イメージを表示し続けたいのでImageList_DragEnterには
            // ListBoxのHandleを渡すのでなく、FormのHandleを渡す
            DragImage.ImageList_DragEnter(this.Handle, x, y);
        }

        private void lstSource_GiveFeedback(object sender,
                                           System.Windows.Forms.GiveFeedbackEventArgs e)
        {
            e.UseDefaultCursors = false;
        }

        private void lstSource_QueryContinueDrag(object sender,
                                        System.Windows.Forms.QueryContinueDragEventArgs e)
        {
            // ImageList_DragEnter同様Windowにおける相対座標を指定する
            int x = Cursor.Position.X - this.Left;
            int y = Cursor.Position.Y - this.Top;
            DragImage.ImageList_DragMove(x, y);
        }

        #endregion


        #region lstDest側の処理

        private void lstDest_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
        {
            if(e.Data.GetDataPresent(typeof(string)))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }

        private void lstDest_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
        {
            DragImage.ImageList_DragLeave(this.Handle);
            if(e.Data.GetDataPresent(typeof(string)))
            {
                string itemText = (string) e.Data.GetData(typeof(string));
                ((ListBox)sender).Items.Add(itemText);
            }
        }

        #endregion
    }
}
スナップショット

なお、コード中に使われているDragImageというクラスは単純にWin32 APIを宣言したクラスなので、 サンプルコードを参考にしてください。

出会い 出会い 出会い系 出会い 出会い 出会い 出会い 出会い 出会い オオクワガタ 出会い 出会い 出会い 出会い アクセスカウンター 出会い 出会い 出会い系 出会い 出会い 出会い 出会い 出会い 出会い