ゴースト付きドラッグ・アンド・ドロップ(D&D)
概要
Windows 7のデスクトップのアイコンをドラッグするとアイコンが半透明になってついてくる(正式名称があるのか知らないが,ゴーストと呼ぶことにする).今回はこれを実現する.
利用例
ドラッグを開始する前に,DragGhostクラスのインスタンスを作成し,Showメソッドを呼び出す.ドラッグ終了時にはRemoveメソッドを呼び出す.また,QueryContinueDragイベントでDragGhostの座標の更新処理を行う.
+
|
DragGhost利用例(C#) |
DragGhost利用例(C#)
protected DragGhost ghost;
private void OnMouseDown(object s, MouseButtonEventArgs e)
{
var sender = s as UIElement;
if ( ghost == null )
{
// DragGhostのインスタンス作成
// 引数は
// 1. 作成するゴースト(ドラッグされる物)
// 2. ゴーストの透明度
// 3. マウスカーソルがゴーストを掴む位置(オプション)
ghost = new DragGhost(sender, 0.5, e.GetPosition(sender));
// ゴーストを表示
ghost.Show();
}
// D&Dを開始
DragDrop.DoDragDrop( ... );
if ( ghost != null )
// ゴーストを削除
ghost.Remove();
ghost = null;
}
private void ContinueDrag(object s, QueryContinueDragEventArgs e)
{
if (ghost != null)
// ゴーストの位置を更新
ghost.Position = CursorInfo.GetNowPosition(this);
}
|
+
|
DragGhost利用例(XAML) |
DragGhost利用例(XAML)
<UIElement MouseDown="OnMouseDown" QueryContinueDrag="ContinueDrag">...</UIElement>
|
利用するクラス
利用するクラスはCursorInfoとDragGhostの2つ.CursorInfoは情報元サイトと全く同じで,DragGhostは情報元サイトのDragAdornerクラスを自分の使いやすいように改造している.
+
|
CursorInfo |
CursorInfo.cs
using System;
using System.Windows;
using System.Windows.Interop;
using System.Runtime.InteropServices;
namespace WpfSampleApplication
{
class CursorInfo
{
[DllImport("user32.dll")]
private static extern void GetCursorPos(out POINT pt);
[DllImport("user32.dll")]
private static extern int ScreenToClient(IntPtr hwnd, ref POINT pt);
private struct POINT
{
public UInt32 X;
public UInt32 Y;
}
public static Point GetNowPosition(System.Windows.Media.Visual v)
{
POINT p;
GetCursorPos(out p);
var source = HwndSource.FromVisual(v) as HwndSource;
var hwnd = source.Handle;
ScreenToClient(hwnd, ref p);
return new Point(p.X, p.Y);
}
}
}
|
+
|
DragGhost.cs |
DragGhost.cs
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Documents;
namespace WpfSampleApplication
{
public class DragGhost
: Adorner
{
protected UIElement ghost;
protected Vector movement;
protected bool isRendering;
public DragGhost(UIElement draggedElement, double opacity)
: this(draggedElement, opacity, new Point(0,0))
{ }
public DragGhost(UIElement draggedElement, double opacity, Point clicked)
: base(draggedElement)
{
isRendering = false;
// 1. ゴーストを作成
// draggedElementを囲むサイズの長方形領域を作成
var b = VisualTreeHelper.GetDescendantBounds(draggedElement);
var rect = new System.Windows.Shapes.Rectangle() { Width = b.Width, Height = b.Height };
// rectを指定の透過度のdraggedElementで塗りつぶすブラシ
var brush = new VisualBrush(draggedElement) { Opacity = opacity };
// rectに ブラシを適用
rect.Fill = brush;
// rectをghostとして覚えておく
ghost = rect;
// 2. ゴーストの描画位置を決定するための前準備
// clickedのクライアント座標を求め反転させたものを移動量とする
movement = (Vector) Window.GetWindow(draggedElement).PointFromScreen(draggedElement.PointToScreen(clicked));
movement.Negate();
}
private Point _Position;
public Point Position{
get { return _Position; }
set
{
// 現在のマウス位置(クライアント座標系)に移動量を加算したものがゴーストの描画位置
_Position = (Point)( value + movement );
UpdatePosition();
}
}
/// <summary>
/// ゴーストの描画を開始する。
/// DragDrop.DoDragDropメソッドを呼び出す直前に実行する。
/// </summary>
public bool Show()
{
if (!isRendering)
{
var adorner = AdornerLayer.GetAdornerLayer(this.AdornedElement);
if (adorner != null)
{
isRendering = true;
adorner.Add(this);
}
}
return isRendering;
}
/// <summary>
/// ゴーストの描画を終了する。
/// DragDrop.DoDragDropメソッドを呼び出した直後に実行する。
/// </summary>
public void Remove()
{
if (isRendering)
{
var adorner = this.Parent as AdornerLayer;
if (adorner != null)
adorner.Remove(this);
isRendering = false;
}
}
protected void UpdatePosition()
{
// Adornerの親はAdornerLayer
var adorner = this.Parent as AdornerLayer;
if (adorner != null)
{
adorner.Update(this.AdornedElement);
}
}
protected override Visual GetVisualChild(int index)
{
return ghost;
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Size MeasureOverride(Size finalSize)
{
ghost.Measure(finalSize);
return ghost.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
ghost.Arrange(new Rect(ghost.DesiredSize));
return finalSize;
}
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
{
var result = new GeneralTransformGroup();
result.Children.Add(base.GetDesiredTransform(transform));
result.Children.Add(new TranslateTransform(Position.X, Position.Y));
return result;
}
}
}
|
拡張版DragGhost
上記を拡張して,以下の機能を追加した.
- ドラッグされた要素と異なるエレメントをゴーストにできるように
- マウスの位置がゴーストの下段中央(などの指定位置)にくる という指定をできるように
ソースコードは以下の通り.GhostAlignmentで2.のマウスの位置を指定する.基本的な使い方は上と同じ.
+
|
DragGhost.cs 拡張版 |
DragGhost.cs 拡張版
public class DragGhost
: Adorner
{
public DragGhost(UIElement draggedElement, double opacity)
: this(draggedElement, null, opacity, null, null)
{ }
public DragGhost(UIElement draggedElement, double opacity, Point clicked)
: this(draggedElement, null, opacity, null, clicked)
{ }
public DragGhost(UIElement draggedElement, double opacity, GhostAlignment? align)
: this(draggedElement, null, opacity, align, null)
{ }
public DragGhost(UIElement draggedElement, double opacity, GhostAlignment? align, Point additional)
: this(draggedElement, null, opacity, align, additional)
{ }
public DragGhost(UIElement draggedElement, UIElement draggedGhost, double opacity)
: this(draggedElement, draggedGhost, opacity, null, null)
{ }
public DragGhost(UIElement draggedElement, UIElement draggedGhost, double opacity, GhostAlignment align)
: this(draggedElement, draggedGhost, opacity, align, null)
{ }
public DragGhost(UIElement draggedElement, UIElement draggedGhost, double opacity, GhostAlignment? align, Point? additional)
: base(draggedElement)
{
if (draggedElement == null)
throw new NullReferenceException("ドラッグされた要素が指定されていません.");
draggedGhost = draggedGhost ?? draggedElement;
if (opacity < 0)
opacity = 0D;
else if (opacity > 1)
opacity = 1D;
align = align ?? GhostAlignment.TopLeft;
additional = additional ?? new Point(0, 0);
isRendering = false;
// 1. ゴーストを作成
// ghostを囲むサイズの長方形領域を作成
var b = VisualTreeHelper.GetDescendantBounds(draggedGhost);
var rect = new System.Windows.Shapes.Rectangle() { Width = b.Width, Height = b.Height };
// rectを指定の透過度のdraggedElementで塗りつぶすブラシ
var brush = new VisualBrush(draggedGhost) { Opacity = opacity };
// rectに ブラシを適用
rect.Fill = brush;
// rectをghostとして覚えておく
ghost = rect;
// 2. ゴーストの描画位置を決定するための前準備
// draggedElementのクライアント座標を求める
movement = (Vector)Window.GetWindow(draggedElement).PointFromScreen(draggedElement.PointToScreen(new Point(0,0)));
// alignmentによる処理
switch (align)
{
case GhostAlignment.TopLeft:
break;
case GhostAlignment.TopCenter:
movement.X += rect.Width / 2;
break;
case GhostAlignment.TopRight:
movement.X += rect.Width;
break;
case GhostAlignment.MiddleLeft:
movement.Y += rect.Height / 2;
break;
case GhostAlignment.MiddleCenter:
movement.X += rect.Width / 2;
movement.Y += rect.Height / 2;
break;
case GhostAlignment.MiddleRight:
movement.X += rect.Width;
movement.Y += rect.Height / 2;
break;
case GhostAlignment.BottomLeft:
movement.Y += rect.Height;
break;
case GhostAlignment.BottomCenter:
movement.X += rect.Width / 2;
movement.Y += rect.Height;
break;
case GhostAlignment.BottomRight:
movement.X += rect.Width;
movement.Y += rect.Height;
break;
}
// additional
movement += (Vector) additional;
movement.Negate();
}
private Point _Position;
public Point Position
{
get { return _Position; }
set
{
// 現在のマウス位置(クライアント座標系)に移動量を加算したものがゴーストの描画位置
_Position = (Point)(value + movement);
UpdatePosition();
}
}
/// <summary>
/// ゴーストの描画を開始する。
/// DragDrop.DoDragDropメソッドを呼び出す直前に実行する。
/// </summary>
public bool Show()
{
if (!isRendering)
{
var adorner = AdornerLayer.GetAdornerLayer(base.AdornedElement);
if (adorner != null)
{
isRendering = true;
adorner.Add(this);
}
}
return isRendering;
}
/// <summary>
/// ゴーストの描画を終了する。
/// DragDrop.DoDragDropメソッドを呼び出した直後に実行する。
/// </summary>
public void Remove()
{
if (isRendering)
{
var adorner = this.Parent as AdornerLayer;
if (adorner != null)
adorner.Remove(this);
isRendering = false;
}
}
protected void UpdatePosition()
{
// Adornerの親はAdornerLayer
var adorner = this.Parent as AdornerLayer;
if (adorner != null)
{
adorner.Update(this.AdornedElement);
}
}
protected override Visual GetVisualChild(int index)
{
return ghost;
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Size MeasureOverride(Size finalSize)
{
ghost.Measure(finalSize);
return ghost.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
ghost.Arrange(new Rect(ghost.DesiredSize));
return finalSize;
}
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
{
var result = new GeneralTransformGroup();
result.Children.Add(base.GetDesiredTransform(transform));
result.Children.Add(new TranslateTransform(Position.X, Position.Y));
return result;
}
protected UIElement ghost;
protected Vector movement;
protected bool isRendering;
}
|
+
|
GhostAlignment.cs |
GhostAlignment.cs
public enum GhostAlignment
{
BottomCenter,
BottomLeft,
BottomRight,
MiddleCenter,
MiddleLeft,
MiddleRight,
TopCenter,
TopLeft,
TopRight
}
|