atwiki-logo
  • 新規作成
    • 新規ページ作成
    • 新規ページ作成(その他)
      • このページをコピーして新規ページ作成
      • このウィキ内の別ページをコピーして新規ページ作成
      • このページの子ページを作成
    • 新規ウィキ作成
  • 編集
    • ページ編集
    • ページ編集(簡易版)
    • ページ名変更
    • メニュー非表示でページ編集
    • ページの閲覧/編集権限変更
    • ページの編集モード変更
    • このページにファイルをアップロード
    • メニューを編集
    • 右メニューを編集
  • バージョン管理
    • 最新版変更点(差分)
    • 編集履歴(バックアップ)
    • アップロードファイル履歴
    • ページ操作履歴
  • ページ一覧
    • ページ一覧
    • このウィキのタグ一覧
    • このウィキのタグ(更新順)
    • このページの全コメント一覧
    • このウィキの全コメント一覧
    • おまかせページ移動
  • RSS
    • このウィキの更新情報RSS
    • このウィキ新着ページRSS
  • ヘルプ
    • ご利用ガイド
    • Wiki初心者向けガイド(基本操作)
    • このウィキの管理者に連絡
    • 運営会社に連絡(不具合、障害など)
プログラミング図書館・本館
  • ウィキ募集バナー
  • 目安箱バナー
  • 操作ガイド
  • 新規作成
  • 編集する
  • 全ページ一覧
  • 登録/ログイン
ページ一覧
プログラミング図書館・本館
  • ウィキ募集バナー
  • 目安箱バナー
  • 操作ガイド
  • 新規作成
  • 編集する
  • 全ページ一覧
  • 登録/ログイン
ページ一覧
プログラミング図書館・本館
  • 新規作成
  • 編集する
  • 登録/ログイン
  • 管理メニュー
管理メニュー
  • 新規作成
    • 新規ページ作成
    • 新規ページ作成(その他)
      • このページをコピーして新規ページ作成
      • このウィキ内の別ページをコピーして新規ページ作成
      • このページの子ページを作成
    • 新規ウィキ作成
  • 編集
    • ページ編集
    • ページ編集(簡易版)
    • ページ名変更
    • メニュー非表示でページ編集
    • ページの閲覧/編集権限変更
    • ページの編集モード変更
    • このページにファイルをアップロード
    • メニューを編集
    • 右メニューを編集
  • バージョン管理
    • 最新版変更点(差分)
    • 編集履歴(バックアップ)
    • アップロードファイル履歴
    • ページ操作履歴
  • ページ一覧
    • このウィキの全ページ一覧
    • このウィキのタグ一覧
    • このウィキのタグ一覧(更新順)
    • このページの全コメント一覧
    • このウィキの全コメント一覧
    • おまかせページ移動
  • RSS
    • このwikiの更新情報RSS
    • このwikiの新着ページRSS
  • ヘルプ
    • ご利用ガイド
    • Wiki初心者向けガイド(基本操作)
    • このウィキの管理者に連絡
    • 運営会社に連絡する(不具合、障害など)
  • atwiki
  • プログラミング図書館・本館
  • アプリケーション内のドラッグアンドドロップ

アプリケーション内のドラッグアンドドロップ

最終更新:2011年01月25日 13:22

atachi

- view
管理者のみ編集可
  • Thumbコントロールによるマウスキャプチャ [#p5c1ebaa]
  • ドラッグアンドドロップの実装例 [#m1444451]
  • 事前準備 [#p5bcb0c1]
  • DragAndDropManagerの実装方針 [#x841d994]
    • ドラッグソースとドラッグターゲット [#z002e782]
    • フックするマウスイベント [#j8b27d19]
  • ドラッグソースの実装 [#n00a2a57]
  • ドラッグの開始時に行う処理 [#da9319ee]
  • ドラッグ中の処理 [#u70ba048]
    • DragDropの使用 [#oe107e55]
    • Drag関係のイベントハンドラの実装 [#z9764f19]
  • ドラッグターゲットの実装 [#z2c98887]
  • Adornerの実装
    • DragAdornerの実装 [#fc1a6a2d]
    • Adornerの表示 [#a5f7159c]
  • ソースコード
    • DragAndDropManager.cs

WPFでは要素のドラッグアンドドロップがサポートされていないので、プログラマが自分で実装する必要があります。

ドロップについてはサポートされており、通常はWindowsのファイルをエクスプローラーなどから受け取ることを目的とした機能のようです。

Thumbコントロールによるマウスキャプチャ

ThumbコントロールはWPFで実装されているドラッグアンドドロップっぽい動きをしそうなコントロールです。
WPFのVisualTreeを解析してみるとスクロールバーなどで使用されていることがわかります。

このコントロールは、マウスのキャプチャやドラッグ、リリースのタイミングでイベントを発行するためそれぞれのタイミングで行いたい処理を実装することができます。

ただし、MSDNには

Thumb コントロールは、ドラッグ アンド ドロップ機能を提供しません。

とあります。

Thumbコントロールは、単にマウスキャプチャできるコントロールといえます。
Thumbコントロールを配置したパネル内で、マウスを使ってコントロールを移動することはできますが、配置したパネルを飛び出して他のパネルへデータを運ぶといった動作はできません。

ドラッグアンドドロップの実装例

多くのドラッグアンドドロップを実装するライブラリでは、singletonなドラッグアンドドロップマネージャクラス(DragAndDropManager)を定義します。
DragAndDropManagerクラスはドラッグ中のアイテムを保持したり、ドラッグ元とドラッグ先の設定を管理します。

WPFではドラッグの開始とドラッグ中のアイテムのプレビュー(Adornerとよばれる)の実装はプログラマが行います。
ドラッグが開始されてからドロップまでは.NET FrameworksのDragDropクラスが面倒をみてくれます。(といっても、ドラッグアンドドロップの情報だけ・・・)

事前準備

このページで説明しているDragAndDropManagerの名前空間はすべて「DDDevelop」です。

DragAndDropManagerの実装方針

DragAndDropManagerはsingletonなインスタンスを保持し、ドラッグ中のアイテムなどの情報を持つ。

ドラッグソースとドラッグターゲット

ドラッグソースとはドラッグを開始するPanelの事です。Panelクラスは.NET Franeworksで背景を持つUIElementです。

ドラッグターゲットとはドラッグしたデータをドロップすることのできるPanelです。

今回実装するドラッグアンドドロップは、ドラッグソースがドラッグが可能であることを設定するところ(IsAllowDrag)から開始します。

フックするマウスイベント

DaDMgrがフックするマウスイベントには注意が必要で、WPFのイベントにはバブルイベントとトンネルイベントの2種類あります。

DaDMgrはドラッグソースが受け取ったマウスイベントを処理しなければならないため、 MouseDownやMouseOverをバブルイベントで受け取ろうとすると、ドラッグソースまでイベントが到達しない可能性があります。 (イベントのイベントルーティング項を参照)

DaDMgrは必ずトンネルイベントでマウスイベントを受け取る必要があります。

ドラッグソースの実装

ドラッグソースとなるPanelは次のようにDragAndDropManager.IsAllowDragプロパティにtrueを設定する。

<Grid Name="DragSourceGrid"
	  DaD:DragAndDropManager.IsAllowDrag="true">
</Grid>

DragAndDropManagerは次のような依存関係プロパティを定義しており、true(または、false)の値を設定されるたびにIsAllodDragChangedハンドラが呼び出される。

public static readonly DependencyProperty IsAllowDragProperty =
	DependencyProperty.RegisterAttached("IsAllowDrag",
		 typeof(bool), 
		 typeof(DragAndDropManager), 
		 new UIPropertyMetadata(false, IsAllowDragChanged));
 
public static bool GetIsAllowDrag (DependencyObject obj) {
	return (bool)obj.GetValue(IsAllowDragProperty);
}
 
public static void SetIsAllowDrag(DependencyObject obj, bool value) {
	obj.SetValue(IsAllowDragProperty, value);
}
 
// イベントハンドラ
private static void IsAllowDragChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
	// ... 
}

IsAllowDragChangedメソッドではドラッグの開始を行うためのマウスイベント(マウスの左クリックイベント:PreviewMouseLeftButtonDown)などのリスナを設定します。
実装については次項から行っていきます。

ドラッグの開始時に行う処理

D&Dの開始はドラッグソース内の特定のPanel内でマウスの左クリックが押された時点からとします。

<Window
	x:Class="DDDevelop.MainWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:dd="clr-namespace:DDDevelop"
	Title="MainWindow" Height="350" Width="525">
	<Grid>
		<Grid Name="grid_DragSource"
			  HorizontalAlignment="Left" Margin="12,12,0,12"  Width="200" Background="#FFFFEBEB"
			  DD:DragAndDropManager.IsAllowDrag="True"
		>
			<Canvas Height="20" HorizontalAlignment="Left" Margin="108,90,0,0" Name="canvas1" VerticalAlignment="Top" Width="20" Background="#FF6572FF" />
		</Grid>
	</Grid>
</Window>
#ref error :ご指定のファイルが見つかりません。ファイル名を確認して、再度指定してください。 (DD1.jpg)

青い矩形の中でマウスの左ボタンを押すとドラッグを開始し、ボタンを放すとドラッグが終了する。という動作を実装します。

// DragAndDropManager.cs
 
private static void IsAllowDragChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
	this.dragSourcePanel = sender as Panel;
	Contract.Requires(dragSourcePanel != null, "ドラッグソースを設定できるのはPanel型のみです");
 
	if(Equals(e.NewValue,true)){
		// ドラッグソース内でマウスの左ボタンが押されたとき
		dragSourcePanel.PreviewMouseLeftButtonDown += Instance.OnDragSource_PreviewMouseLeftButtonDown;
 
		// ドラッグソース内でマウスの左ボタンが離されたとき
		dragSourcePanel.PreviewMouseLeftButtonUp += Instance.OnDragSource_PreviewMouseLeftButtonUp;
	}else {
		// ドラッグソースではなくなった場合、リスナ登録を解放する
 
		dragSourcePanel.PreviewMouseLeftButtonDown -= Instance.OnDragSource_PreviewMouseLeftButtonDown;
		dragSourcePanel.PreviewMouseLeftButtonUp -= Instance.OnDragSource_PreviewMouseLeftButtonUp;
	}
}
 
 
private void OnDragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
	Panel dragSourcePanel = sender as Panel;
	Contract.Requires(dragSourcePanel != null, "ドラッグソースが不正です");
 
	if (dragSourcePanel.Children.Contains(e.OriginalSource as FrameworkElement) ){ // イベントのソースがドラッグソース内にあるか?
		rootWindow = Window.GetWindow(dragSourcePanel);
 
		startPoint = e.GetPosition(rootWindow); // ドラッグを開始しした座標
 
		dragData = "何かドラッグするデータ";
 
		Debug.WriteLine("D&Dを開始");
	}
}
 
private void OnDragSource_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
	// D&D処理を中止とする
	dragData = null;
	Debug.WriteLine("D&Dを中止"); // ※ 今のところ何も処理はないので、デバッグメッセージだけを出力
}
 
 
// ※DragAndDropManagerにインスタンス変数を追加
private Point startPoint;
private Window rootWindow;
Panel dragSourcePanel;
private object dragData;

IsAllowDragChangedメソッドに追加した処理は、IsAlloDragプロパティが「true」に設定された場合にドラッグソースにイベントリスナを登録し、「false」に設定された場合はドラッグソースに登録したイベントリスナを解放します。

OnDragSource_PreviewMouseLeftButtonDownメソッドはドラッグソース内でマウスの左ボタンが押された瞬間に呼び出されるイベントハンドラです。
このメソッドでは、MouseButtonEventArgs.OriginalSourceプロパティを判断しドラッグソース内の子要素でイベントが発生しているかチェックします。子要素内でイベントが発生している場合はドラッグを開始します。 そのため、図の青い矩形内で左ボタンを押すと「D&Dを開始」と出力されますが、それ以外の部分で左クリックを押してもなにも起きません。

ドラッグ中の処理

次にドラッグ中の処理を追加します。
ドラッグが開始されたら、DragAndDropManagerはマウスの動きをキャプチャします。

ドラッグソース内でのマウス移動イベントを処理するイベントハンドラを追加します。

private static void IsAllowDragChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
	Panel dragSourcePanel = sender as Panel;
	Contract.Requires(dragSourcePanel != null, "ドラッグソースを設定できるのはPanel型のみです");
 
	if (Equals(e.NewValue, true)) {
		dragSourcePanel.PreviewMouseLeftButtonDown += Instance.OnDragSource_PreviewMouseLeftButtonDown;
		dragSourcePanel.PreviewMouseLeftButtonUp += Instance.OnDragSource_PreviewMouseLeftButtonUp;
 
		// ↓追加
		dragSourcePanel.PreviewMouseMove += Instance.OnDragSource_PreviewMouseMove;
	}
	else {
		dragSourcePanel.PreviewMouseLeftButtonDown -= Instance.OnDragSource_PreviewMouseLeftButtonDown;
		dragSourcePanel.PreviewMouseLeftButtonUp -= Instance.OnDragSource_PreviewMouseLeftButtonUp;
 
		// ↓追加
		dragSourcePanel.PreviewMouseMove -= Instance.OnDragSource_PreviewMouseMove;
	}
}
 
private void OnDragSource_PreviewMouseMove(object sender, MouseEventArgs e) {
	if (dragData != null) { // ドラッグしているデータがあるかどうか。
		Debug.WriteLine("ドラッグ中 " + DateTime.Now);
	}
}

ドラッグを開始するとマウスの左クリックを押しながらマウスを動かすと「ドラッグ中 2010/07/01 11:15:12」のような出力が永遠と出力されます。(マウスの動きを止めると、PreviewMouseMoveイベントが発行されなくなるので表示は止まる)

ただし、PreviewMouseMoveイベントを発行はドラッグソース内でマウスを動かしている間のみ行われるので、ドラッグ中にドラッグソース外へマウスカーソルを移動してしまうと、PreviewMouseLeftButtonUpやPreviewMouseMoveが発行されなくなり、おかしな動作になってしまいます。

DragDropの使用

ドラッグに関する処理は.NET Frameworksでもサポートする処理が実装されています。
DragDropクラスを使用すると、システムレベルのドロップをサポートすることができます。

ドラッグ中の処理はDragDrop.DoDragDrop()を使って行うのですが、DragDrop.DoDragDrop()の使用には注意する必要があります。DragDrop.DoDragDrop()を呼び出すとそのスレッドはDragDropによってドラッグが終了されるまでブロックされます。


DragDropを使用するとUIElementクラスのDragEnter/DragOver/DragLeaveが呼び出されるようになります。(AllowDropがセットされている場合)

private void OnDragSource_PreviewMouseMove(object sender, MouseEventArgs e) {
	if (dragData != null) {
		//
		// rootWindowのD&Dに関する設定
		bool oldAllowDrop = this.rootWindow.AllowDrop;
		rootWindow.AllowDrop = true;
		rootWindow.DragEnter += OnRootWindow_DragEnter; // ※後述
		rootWindow.DragOver += OnRootWindow_DragOver;   // ※後述
		rootWindow.DragLeave += OnRootWindow_DragLeave; // ※後述
 
		// ドラッグアンドドロップのデータ
		// DataObjectクラスを使用する。
		DataObject dddata = new DataObject(DataFormats.StringFormat, this.dragData);
 
 
		//------------------
		// ドラッグアンドドロップ開始
 
		DragDropEffects effects = DragDrop.DoDragDrop(
									(DependencyObject)sender,
									dddata,
									DragDropEffects.Move);
 
		// DragDrop.DoDragDropは、ドラッグが終了するまでスレッドはブロックされる
 
 
		//----------------
		// ドラッグアンドドロップ終了
 
		//
		// rootWindowのD&Dに関する設定を復帰
		this.rootWindow.AllowDrop = oldAllowDrop; // AllowDropの値を元に戻す
		rootWindow.DragEnter -= OnRootWindow_DragEnter;
		rootWindow.DragOver -= OnRootWindow_DragOver;
		rootWindow.DragLeave -= OnRootWindow_DragLeave;
 
		this.dragData = null;
	}
}

ドラッグアンドドロップを行うデータ形式については、任意のオブジェクトか、他のアプリケーションへもデータを渡すことができるDataObjectクラスを使用します。D&Dがアプリケーション内だけで完結するのならば好きな型で問題ありません。
他のアプリケーションにデータを渡すようなD&Dの場合には、共通のデータ形式名をDataObjectに渡すことで、ドロップ先のアプリケーションがそのデータ形式名のデータを受け取るように実装されていれば、アプリケーション間でD&Dを使ってデータの引き渡しができます。

今回はアプリケーション内でのD&Dの説明ということで、D&Dのデータ形式についてはこれ以上は追求しません。

Drag関係のイベントハンドラの実装

rootWindowのDragEnter・DragOver・DragLeaveに設定したイベントハンドラです。
具体的な実装はまだ行っていませんが、デバッグプリントによってある程度の流れはつかめるハズです。

e.Handledはイベントルーティングを停止するかのフラグです。
DragEnter/DragOver/DragLeaveはバブル型イベントで、今回の場合VisualTreeの下位階層にイベントを伝える必要がないため、これ以上のイベントルーティングを停止させています。

private void OnRootWindow_DragEnter(object sender, DragEventArgs e) {
	Debug.WriteLine("OnRootWindow_DragEnter");
 
	e.Effects = DragDropEffects.None; // マウスカーソルの形状をドロップ不可能に。
	e.Handled = true;                 // イベントを処理したことを通知(イベントルーティングの停止)
}
 
private void OnRootWindow_DragOver(object sender, DragEventArgs e) {
	Debug.WriteLine("OnRootWindow_DragOver");
 
 
	e.Effects = DragDropEffects.None; // マウスカーソルの形状をドロップ不可能に。
	e.Handled = true;                 // イベントを処理したことを通知(イベントルーティングの停止)
}
 
private void OnRootWindow_DragLeave(object sender, DragEventArgs e) {
	Debug.WriteLine("OnRootWindow_DragLeave");
 
	e.Handled = true; // イベントを処理したことを通知(イベントルーティングの停止)
}

ドラッグターゲットの実装

ドラッグターゲットとはD&Dのドロップ先のことです。
これまではドラッグを開始するための実装を行ってきましたが、次にドラッグ中のデータを受け取るための受け取り側の実装を行います。

ドラッグ可能なパネルにはIsAllowDropプロパティを「true」とすることで、ドラッグターゲットとしてDragAndDropManagerに登録します。

ドロップターゲットでのマウス処理は、DragDrop.DoDragDrop()を使用しているためマウスイベントを処理するにではなくDragEnterやDragLeaveなどのイベントを処理します。

ドロップターゲットの実装はドロップソースの時と同じなので、詳しい説明は省略します。

public static readonly DependencyProperty IsAllowDropProperty =
			DependencyProperty.RegisterAttached("IsAllowDrop",
			typeof(bool),
			typeof(DragAndDropManager),
			new UIPropertyMetadata(false, IsAllowDropChanged));
 
public static bool GetIsAllowDrop(DependencyObject obj) {
	return (bool)obj.GetValue(IsAllowDropProperty);
}
 
public static void SetIsAllowDrop(DependencyObject obj, bool value) {
	obj.SetValue(IsAllowDropProperty, value);
}
 
 
private static void IsAllowDropChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
	Panel dragTargetPanel = sender as Panel;
	Contract.Requires(dragTargetPanel != null, "ドラッグターゲットを設定できるのはPanel型のみです");
 
	if (Equals(e.NewValue, true)) {
		dragTargetPanel.AllowDrop = true;
 
		dragTargetPanel.PreviewDragEnter += Instance.OnDragTarget_PreviewDragEnter;
		dragTargetPanel.PreviewDragOver += Instance.OnDragTarget_PreviewDragOver;
		dragTargetPanel.PreviewDragLeave += Instance.OnDragTarget_PreviewDragLeave;
		dragTargetPanel.PreviewDrop += Instance.OnDragTarget_PreviewDrop;
	}
	else {
		dragTargetPanel.AllowDrop = false;
 
		dragTargetPanel.PreviewDragEnter -= Instance.OnDragTarget_PreviewDragEnter;
		dragTargetPanel.PreviewDragOver -= Instance.OnDragTarget_PreviewDragOver;
		dragTargetPanel.PreviewDragLeave -= Instance.OnDragTarget_PreviewDragLeave;
		dragTargetPanel.PreviewDrop -= Instance.OnDragTarget_PreviewDrop;
	}
}
 
private void OnDragTarget_PreviewDrop(object sender, DragEventArgs e) {
	object draggedItem = e.Data.GetData(DataFormats.StringFormat);
	if (draggedItem != null) {
		// TODO: ドロップしたときの処理を追加する
 
		e.Handled = true; // イベントを処理したことを通知
	}
}
 
private void OnDragTarget_PreviewDragOver(object sender, DragEventArgs e) {
	object draggedItem = e.Data.GetData(DataFormats.StringFormat);
	if (draggedItem != null) {
	}
	e.Handled = true;
}
 
private void OnDragTarget_PreviewDragEnter(object sender, DragEventArgs e) {
	object draggedItem = e.Data.GetData(DataFormats.StringFormat);
	if (draggedItem != null) {
	}
	e.Handled = true;
}
 
private void OnDragTarget_PreviewDragLeave(object sender, DragEventArgs e) {
	object draggedItem = e.Data.GetData(DataFormats.StringFormat);
	if (draggedItem != null) {
 
	}
	e.Handled = true;
}

ドラッグターゲットがD&Dされてきたデータに対して何らかの処理を追加する箇所が、OnDragTarget_PreviewDropとなります。

OnDragTarget_PreviewDragOver/OnDragTarget_PreviewDragEnter/OnDragTarget_PreviewDragLeaveは今のところ追加する処理はありません。
ここには、まだ実装していないD&Dの重要な部分であるAdornerの制御を記述します。

Adornerの実装

これまで実装してきたD&Dは、確かにD&Dとしてデータのやりとりができていましたが、ビジュアルの実装を行っていません。
Adornerを実装してよりD&Dらしい実装となります。

Adornetとは、Windowsエクスプローラーでファイルをドラッグアンドドロップ使用とした場合にマウスに追従するものを指します。

Adornerの実装にはSystem.Windows.Documents.Adornerを継承して作成します。
Adornerレイヤーというものを、AdornerLayerから取得しそこにAdornerを表示します。

描画する画像に関してはAdornerクラスを継承したクラスを作成しその中で行います。

DragAdornerの実装

public class DragAdorner : Adorner {
	//=====================================================================
	#region Constructors
	//=====================================================================
 
	/// <summary>
	/// 
	/// </summary>
	/// <param name="owner"></param>
	/// <param name="adornElement"></param>
	public DragAdorner(UIElement owner, UIElement adornElement)
		: base(owner) {
		_Owner = owner;
 
		Brush brush = new VisualBrush(adornElement);
 
		var rectangle = new Rectangle {
			Width = adornElement.RenderSize.Width,
			Height = adornElement.RenderSize.Height,
			Fill = brush,
			Opacity = 0.7
		};
 
		Point mousePoint = Mouse.PrimaryDevice.GetPosition(adornElement);
		XCenter = mousePoint.X;
		YCenter = mousePoint.Y;
 
		_Child = rectangle;
	}
 
	#endregion Constructors
 
 
 
	/// <summary>
	/// Adornerの表示座標
	/// </summary>
	/// <param name="left"></param>
	/// <param name="top"></param>
	public void SetPosition(double left, double top) {
		_TopOffset = top;
		_LeftOffset = left;
 
		UpdatePosition();
	}
 
 
	//=====================================================================
	#region Properties
	//=====================================================================
 
	public double TopOffset {
		get {
			return _TopOffset;
		}
		set {
			_TopOffset = value - YCenter;
			UpdatePosition();
		}
	}
 
	public double LeftOffset {
		get {
			return _LeftOffset;
		}
		set {
			_LeftOffset = value - XCenter;
			UpdatePosition();
		}
	}
 
	#endregion Properties
 
 
	//=====================================================================
	#region Adorner Methods
	//=====================================================================
	protected override Visual GetVisualChild(int index) {
		return this._Child;
	}
 
	protected override int VisualChildrenCount {
		get {
			return 1;
		}
	}
 
	protected override Size MeasureOverride(Size finalSize) {
		_Child.Measure(finalSize);
		return _Child.DesiredSize;
	}
 
 
	protected override Size ArrangeOverride(Size finalSize) {
		_Child.Arrange(new Rect(finalSize));
		return finalSize;
	}
 
	public override GeneralTransform GetDesiredTransform(GeneralTransform transform) {
		var result = new GeneralTransformGroup();
		result.Children.Add(base.GetDesiredTransform(transform));
		result.Children.Add(new TranslateTransform(_LeftOffset, _TopOffset));
		return result;
	}
	#endregion Adorner Methods
 
 
	private void UpdatePosition() {
		var adorner = (AdornerLayer)Parent;
		if (adorner != null)
			adorner.Update(AdornedElement);
	}
 
 
 
 
	private double _LeftOffset;
	private double _TopOffset;
	protected UIElement _Child;
	protected UIElement _Owner;
	protected double XCenter;
	protected double YCenter;
}

コンストラクタで表示する内容を作成します。
とりあえず、ドラッグするパネルと同じ色と同じサイズの矩形を作成し、それを表示します。

Adornerの表示

DragAndDropManagerにAdornerを表示するための処理を追加します。

// DragAndDropManager.cs
 
/// <summary>
/// ドラッグ中のAdornerを指定の場所に表示
/// </summary>
/// <param name="currentPosition"></param>
private void ShowDragAdorner(Point currentPosition) {
	if (dragAdorner == null) {
		AdornerLayer layer = AdornerLayer.GetAdornerLayer(dragSourcePanel);
		this.dragAdorner = new DragAdorner(dragSourcePanel, dragSourceItem);
 
		layer.Add(this.dragAdorner);
	}
 
	dragAdorner.SetPosition(currentPosition.X, currentPosition.Y);
}
 
/// <summary>
/// Adornerを非表示
/// </summary>
private void RemoveDragAdorner() {
	if (dragAdorner != null) {
		AdornerLayer layer = AdornerLayer.GetAdornerLayer(dragSourcePanel);
		layer.Remove(dragAdorner);
 
		this.dragAdorner = null;
	}
}
 
DragAdorner dragAdorner;

Adornerはドラッグを行っている最中に表示するものなので、DragOverイベントでShowDragAdorner()を呼び出します。
ShowDragAdorner()は表示する座標を引数として受け取ります。

private void OnRootWindow_DragOver(object sender, DragEventArgs e) {
	Debug.WriteLine("OnRootWindow_DragOver");
 
	// ※追加
	// Adornerを表示
	ShowDragAdorner(new Point {
		X = e.GetPosition(rootWindow).X,
		Y = e.GetPosition(rootWindow).Y
	});
 
	e.Effects = DragDropEffects.None; // マウスカーソルの形状をドロップ不可能に。
	e.Handled = true;                 // イベントを処理したことを通知
}
 
private void OnDragTarget_PreviewDragOver(object sender, DragEventArgs e) {
	object draggedItem = e.Data.GetData(DataFormats.StringFormat);
 
	if (draggedItem != null) {
		// ※追加
		// Adornerを表示
		ShowDragAdorner(new Point {
			X = e.GetPosition(rootWindow).X,
			Y = e.GetPosition(rootWindow).Y
		});
 
	}
 
	e.Handled = true;
}

マウスカーソルがDragTargetに設定したパネルの上にある場合は、OnDragTarget_PreviewDragOverが呼び出されます。
それ以外の箇所ではOnRootWindow_DragOverが呼び出されます。

2つのDragOverが呼び出されないのは、DropTargetがトンネル型イベントであるPreviewDragOverをリスナに登録し、そのイベントハンドラ(OnDragTarget_PreviewDragOver)で「e.Handled=true」としているためです。
Handledがtrueとなった場合は、それ以上のイベントルーティングが行われなくなります。

ソースコード

DragAndDropManager.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows.Documents;
 
namespace DDDevelop {
	public class DragAndDropManager {
		private static DragAndDropManager instance;
		public static DragAndDropManager Instance {
			get {
				if (instance == null) {
					instance = new DragAndDropManager();
				}
				return instance;
			}
		}
 
 
		//=====================================================================
		#region Constructors
		//=====================================================================
		/// <summary>
		/// デフォルトコンストラクタ
		/// </summary>
		private DragAndDropManager() {
		}
		#endregion Constructors
 
 
		//=====================================================================
		#region Dependency Properties
		//=====================================================================
		public static readonly DependencyProperty IsAllowDragProperty = 
			DependencyProperty.RegisterAttached("IsAllowDrag",
			typeof(bool),
			typeof(DragAndDropManager),
			new UIPropertyMetadata(false, IsAllowDragChanged));
 
		public static bool GetIsAllowDrag(DependencyObject obj) {
			return (bool)obj.GetValue(IsAllowDragProperty);
		}
 
		public static void SetIsAllowDrag(DependencyObject obj, bool value) {
			obj.SetValue(IsAllowDragProperty, value);
		}
 
		public static readonly DependencyProperty IsAllowDropProperty =
			DependencyProperty.RegisterAttached("IsAllowDrop",
			typeof(bool),
			typeof(DragAndDropManager),
			new UIPropertyMetadata(false, IsAllowDropChanged));
 
		public static bool GetIsAllowDrop(DependencyObject obj) {
			return (bool)obj.GetValue(IsAllowDropProperty);
		}
 
		public static void SetIsAllowDrop(DependencyObject obj, bool value) {
			obj.SetValue(IsAllowDropProperty, value);
		}
 
		#endregion Dependency Properties
 
 
		//=====================================================================
		#region Handlers
		//=====================================================================
 
		private static void IsAllowDragChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
			Panel dragSourcePanel = sender as Panel;
			Contract.Requires(dragSourcePanel != null, "ドラッグソースを設定できるのはPanel型のみです");
 
			if (Equals(e.NewValue, true)) {
				dragSourcePanel.PreviewMouseLeftButtonDown += Instance.OnDragSource_PreviewMouseLeftButtonDown;
				dragSourcePanel.PreviewMouseLeftButtonUp += Instance.OnDragSource_PreviewMouseLeftButtonUp;
				dragSourcePanel.PreviewMouseMove += Instance.OnDragSource_PreviewMouseMove;
			}
			else {
				dragSourcePanel.PreviewMouseLeftButtonDown -= Instance.OnDragSource_PreviewMouseLeftButtonDown;
				dragSourcePanel.PreviewMouseLeftButtonUp -= Instance.OnDragSource_PreviewMouseLeftButtonUp;
				dragSourcePanel.PreviewMouseMove -= Instance.OnDragSource_PreviewMouseMove;
			}
		}
 
 
		private static void IsAllowDropChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
			Panel dragTargetPanel = sender as Panel;
			Contract.Requires(dragTargetPanel != null, "ドラッグターゲットを設定できるのはPanel型のみです");
 
			if (Equals(e.NewValue, true)) {
				dragTargetPanel.AllowDrop = true;
 
				dragTargetPanel.PreviewDragEnter += Instance.OnDragTarget_PreviewDragEnter;
				dragTargetPanel.PreviewDragOver += Instance.OnDragTarget_PreviewDragOver;
				dragTargetPanel.PreviewDragLeave += Instance.OnDragTarget_PreviewDragLeave;
				dragTargetPanel.PreviewDrop += Instance.OnDragTarget_PreviewDrop;
			}
			else {
				dragTargetPanel.AllowDrop = false;
 
				dragTargetPanel.PreviewDragEnter -= Instance.OnDragTarget_PreviewDragEnter;
				dragTargetPanel.PreviewDragOver -= Instance.OnDragTarget_PreviewDragOver;
				dragTargetPanel.PreviewDragLeave -= Instance.OnDragTarget_PreviewDragLeave;
				dragTargetPanel.PreviewDrop -= Instance.OnDragTarget_PreviewDrop;
			}
		}
 
 
		/// <summary>
		/// ドラッグソース内でマウスの左クリックを受けた場合の実行関数
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnDragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
			this.dragSourcePanel = sender as Panel;
 
			if (dragSourcePanel.Children.Contains(e.OriginalSource as FrameworkElement) ){
				dragSourceItem = e.OriginalSource as UIElement;
				rootWindow = Window.GetWindow(dragSourcePanel);
 
				startPoint = e.GetPosition(rootWindow);
 
				dragData = "何かドラッグするデータ";
 
				Debug.WriteLine("D&Dを開始");
			}
		}
 
		/// <summary>
		/// ドラッグソース内でマウスの左クリックが解除された場合の実行関数
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnDragSource_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
			// D&D処理を中止とする
			dragData = null;
			Debug.WriteLine("D&Dを中止");
		}
 
 
		/// <summary>
		/// ドラッグソース内でのマウス移動の実行関数
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnDragSource_PreviewMouseMove(object sender, MouseEventArgs e) {
			if (dragData != null) {
				Panel dragTargetPanel = sender as Panel;
 
				Debug.WriteLine("ドラッグ中 " + DateTime.Now);
 
				// D&Dのデータ
				DataObject dddata = new DataObject(DataFormats.StringFormat, this.dragData);
 
 
				bool oldAllowDrop = this.rootWindow.AllowDrop;
				rootWindow.AllowDrop = true;
				rootWindow.DragEnter += OnRootWindow_DragEnter;
				rootWindow.DragOver += OnRootWindow_DragOver;
				rootWindow.DragLeave += OnRootWindow_DragLeave;
 
				DragDropEffects effects = DragDrop.DoDragDrop((DependencyObject)sender, dddata, DragDropEffects.Move);
 
 
				//----------------
				// D&D終了の処理
 
				RemoveDragAdorner();
 
				rootWindow.AllowDrop = oldAllowDrop;
				rootWindow.DragEnter -= OnRootWindow_DragEnter;
				rootWindow.DragOver -= OnRootWindow_DragOver;
				rootWindow.DragLeave -= OnRootWindow_DragLeave;
 
				this.dragData = null;
			}
		}
 
		private void OnRootWindow_DragEnter(object sender, DragEventArgs e) {
			Debug.WriteLine("OnRootWindow_DragEnter");
 
			e.Effects = DragDropEffects.None; // マウスカーソルの形状をドロップ不可能に。
			e.Handled = true;
		}
 
		private void OnRootWindow_DragOver(object sender, DragEventArgs e) {
			Debug.WriteLine("OnRootWindow_DragOver");
 
			var rootWindow = sender as FrameworkElement;
 
			ShowDragAdorner(new Point {
				X = e.GetPosition(rootWindow).X,
				Y = e.GetPosition(rootWindow).Y
			});
 
 
			e.Effects = DragDropEffects.None; // マウスカーソルの形状をドロップ不可能に。
			e.Handled = true;                 // イベントを処理したことを通知
		}
 
		private void OnRootWindow_DragLeave(object sender, DragEventArgs e) {
			Debug.WriteLine("OnRootWindow_DragLeave");
 
			e.Handled = true;
		}
 
		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnDragTarget_PreviewDrop(object sender, DragEventArgs e) {
			object draggedItem = e.Data.GetData(DataFormats.StringFormat);
			if (draggedItem != null) {
				// TODO: ドロップしたときの処理を追加する
 
				e.Handled = true; // イベントを処理したことを通知
			}
		}
 
		private void OnDragTarget_PreviewDragOver(object sender, DragEventArgs e) {
			object draggedItem = e.Data.GetData(DataFormats.StringFormat);
			if (draggedItem != null) {
				var panel = sender as UIElement;
				ShowDragAdorner(new Point {
					X = e.GetPosition(rootWindow).X,
					Y = e.GetPosition(rootWindow).Y
				});
 
			}
			e.Handled = true;
		}
 
		private void OnDragTarget_PreviewDragEnter(object sender, DragEventArgs e) {
			object draggedItem = e.Data.GetData(DataFormats.StringFormat);
			if (draggedItem != null) {
			}
			e.Handled = true;
		}
 
		private void OnDragTarget_PreviewDragLeave(object sender, DragEventArgs e) {
			object draggedItem = e.Data.GetData(DataFormats.StringFormat);
			if (draggedItem != null) {
 
			}
			e.Handled = true;
		}
 
		#endregion Handlers
 
		/// <summary>
		/// ドラッグ中のAdornerを表示
		/// </summary>
		/// <param name="currentPosition"></param>
		private void ShowDragAdorner(Point currentPosition) {
			if (dragAdorner == null) {
				AdornerLayer layer = AdornerLayer.GetAdornerLayer(dragSourcePanel);
				this.dragAdorner = new DragAdorner(dragSourcePanel, dragSourceItem);
 
				layer.Add(this.dragAdorner);
			}
 
			dragAdorner.SetPosition(currentPosition.X, currentPosition.Y);
		}
 
		private void RemoveDragAdorner() {
			if (dragAdorner != null) {
				AdornerLayer layer = AdornerLayer.GetAdornerLayer(dragSourcePanel);
				layer.Remove(dragAdorner);
 
				this.dragAdorner = null;
			}
		}
 
 
		DragAdorner dragAdorner;
		Point startPoint;
		UIElement dragSourceItem;
		Panel dragSourcePanel;
		Window rootWindow;
		object dragData;
	}
}
「アプリケーション内のドラッグアンドドロップ」をウィキ内検索
LINE
シェア
Tweet
添付ファイル
  • AdornerSample.jpg
  • ExplorerDaD.jpg

[Amazon商品]


プログラミング図書館・本館
記事メニュー

C#

  • 新機能
  • 言語文法
    • 型
    • 関数
    • クラス
    • 演算子
    • 構文
    • デリゲート
    • イベント
    • 例外処理
    • アトリビュート
    • 名前空間
    • ジェネリクス
  • リフレクション
  • LINQ

.NET Frameworks

  • 数値
  • 文字列
  • 日付時刻
  • オブジェクト
  • コレクション
  • ファイルシステム
  • スレッド
  • データテーブル
  • グラフィックス
  • デバッグ
    • ログ出力
    • ユニットテスト
  • ユーティリティ
  • ネットワーク
    • HTTP
  • デバイス
    • COMポート
  • EntityFrameworks
    • Code First Programming Model
  • デバイスドライバ
    • WinUSB

WPF

  • XAML
  • イベント
  • コマンド
  • スタイル
  • データバインディング
  • コンポーネント
  • グラフィックス
  • カスタムコンポーネント
    • 依存関係プロパティ

アプリケーション

  • 二重起動の防止
  • アンマネージドDLLの読み込み
  • 外部のアプリケーションを起動

VisualStudio

  • 拡張機能
  • カラー設定

ライブラリ

  • WPFToolkit
  • A Fast CSV Reader
  • AvalonDock

実装方法

  • ドラッグアンドドロップ
  • アプリケーション内のドラッグアンドドロップ
  • CSVからDataTable

minecraft

  • Bukkit
  • プラグイン
    • Permissions
    • SlowHealth
    • CookieMonster
    • iConomy
    • WorldGuard
    • Lockette
    • MyHome
    • BorderGuard

サイトについて

プロフィール/リンク/未分類

メモ/ゲーム/Stacks/Omoikane


更新履歴

取得中です。

ここを編集

記事メニュー2

読んでいる本


Effective C#

QLOOKアクセス解析

ここを編集

人気記事ランキング
  1. 型
  2. WPF/依存関係プロパティ
  3. WPF/コンポーネント/コントロール/チェックボックスリストボックス
  4. A Fast CSV Reader
  5. CS/リフレクション
  6. .NET/オブジェクト
  7. WPF/コンポーネント/コントロール/リストボックス
  8. WPF/イベント
もっと見る
最近更新されたページ
  • 4983日前

    Bukkit
  • 4989日前

    トップページ
  • 5010日前

    メニュー
  • 5020日前

    WPF/グラフィックス
  • 5020日前

    .NET/グラフィックス
  • 5020日前

    .NET/グラフィックス/画像の書き込み
  • 5020日前

    .NET/グラフィックス/画像の読み込み
  • 5026日前

    .NET/コレクション
  • 5044日前

    .NET/WinUSB
  • 5079日前

    BorderGuardプラグイン
もっと見る
人気記事ランキング
  1. 型
  2. WPF/依存関係プロパティ
  3. WPF/コンポーネント/コントロール/チェックボックスリストボックス
  4. A Fast CSV Reader
  5. CS/リフレクション
  6. .NET/オブジェクト
  7. WPF/コンポーネント/コントロール/リストボックス
  8. WPF/イベント
もっと見る
最近更新されたページ
  • 4983日前

    Bukkit
  • 4989日前

    トップページ
  • 5010日前

    メニュー
  • 5020日前

    WPF/グラフィックス
  • 5020日前

    .NET/グラフィックス
  • 5020日前

    .NET/グラフィックス/画像の書き込み
  • 5020日前

    .NET/グラフィックス/画像の読み込み
  • 5026日前

    .NET/コレクション
  • 5044日前

    .NET/WinUSB
  • 5079日前

    BorderGuardプラグイン
もっと見る
ウィキ募集バナー
新規Wikiランキング

最近作成されたWikiのアクセスランキングです。見るだけでなく加筆してみよう!

  1. MadTown GTA (Beta) まとめウィキ
  2. GTA5 MADTOWN(β)まとめウィキ
  3. R.E.P.O. 日本語解説Wiki
  4. シュガードール情報まとめウィキ
  5. SYNDUALITY Echo of Ada 攻略 ウィキ
  6. ガンダムGQuuuuuuX 乃木坂46部@wiki
  7. ドタバタ王子くん攻略サイト
  8. MADTOWN @ ウィキ
  9. パズル&コンクエスト(Puzzles&Conquest)攻略Wiki
  10. ありふれた職業で世界最強 リベリオンソウル @ ウィキ
もっと見る
人気Wikiランキング

atwikiでよく見られているWikiのランキングです。新しい情報を発見してみよう!

  1. アニヲタWiki(仮)
  2. ストグラ まとめ @ウィキ
  3. ゲームカタログ@Wiki ~名作からクソゲーまで~
  4. 初音ミク Wiki
  5. 機動戦士ガンダム バトルオペレーション2攻略Wiki 3rd Season
  6. 検索してはいけない言葉 @ ウィキ
  7. Grand Theft Auto V(グランドセフトオート5)GTA5 & GTAオンライン 情報・攻略wiki
  8. 発車メロディーwiki
  9. 英傑大戦wiki
  10. SDガンダム ジージェネレーションジェネシス 攻略Wiki
もっと見る
全体ページランキング

最近アクセスの多かったページランキングです。話題のページを見に行こう!

  1. 魔獣トゲイラ - バトルロイヤルR+α ファンフィクション(二次創作など)総合wiki
  2. 参加者一覧 - MadTown GTA (Beta) まとめウィキ
  3. Lycoris - MadTown GTA (Beta) まとめウィキ
  4. 参加者一覧 - ストグラ まとめ @ウィキ
  5. 赤城ウェン - MadTown GTA (Beta) まとめウィキ
  6. ぶんぶんギャング - MadTown GTA (Beta) まとめウィキ
  7. R7高校総体北部支部予選 - 埼玉県高等学校バスケットボール北部支部
  8. 警察 - MadTown GTA (Beta) まとめウィキ
  9. Happy Nutty Bunny - ストグラ まとめ @ウィキ
  10. 乗り物一覧 - Grand Theft Auto V(グランドセフトオート5)GTA5 & GTAオンライン 情報・攻略wiki
もっと見る

  • このWikiのTOPへ
  • 全ページ一覧
  • アットウィキTOP
  • 利用規約
  • プライバシーポリシー

2019 AtWiki, Inc.