ドラッグ中に半透明のイメージを表示させる方法
エクスプローラなどのアプリケーションではファイルをドラッグするときに半透明のイメージが一緒に ドラッグされるようになっています。この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.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を宣言したクラスなので、 サンプルコードを参考にしてください。
