2004-09-03 [長年日記]

OLE DnDでのドラッグイメージの表示

OLD DnDでドラッグイメージを描く方法は以下の方法がありそうです。

  1. ドラッグカーソルに任意のイメージを割り当てる
  2. IDropSourceのGiveFeedbackでターゲット(と思われる)ウィンドウに無理やり描く
  3. IDropTargetのDragOverなどで描くターゲットウィンドウが描く

本来はIDropSourceのGiveFeedbackでは、ドロップされたときにソース側がどのように変更されるかをユーザに示し、IDropTargetのDragOverなどではドロップされたときにターゲット側がどのように変更されるかを示すのがガイドラインです。しかし、ワープロのようなソフトならともかく、普通のソフトの場合、ソース側でイメージを用意しておいて、それをターゲット側で描画したほうが一貫性のあるインターフェイスを構築できそうです。

ではどうやってソースからターゲットにイメージを渡せばいいかと考えると、ドラッグされているIDataObjectに適当なフォーマットでイメージを入れておけば良さそうです。というあたりをやってくれるのがIDropSourceHelperIDropTargetHelperです。使い方はこのあたりに詳しいです。

ドラッグするIDataObjectに任意のフォーマットのデータを保持できるようにしておく必要がありますが、後はシステム側で面倒を見てくれるようになります。また、エクスプローラなどからドラッグした場合にも、ターゲットのウィンドウ上にドラッグイメージが描かれるようになります。

ImageListを使ってドラッグイメージの表示

IDropSourceHelperとIDropTargetHelperを使うと割と手軽にドラッグイメージを描けるようになりますが、少しだけ問題点があります。一つ目はIDataObjectで任意のデータを扱えるようにするのが面倒なこと*1、二つ目はWindows CEではサポートされていないことです*2*3

そんなときにはImageListの機能を使うと簡単にドラッグイメージを描けます。手順としてはこんな感じになります。

  1. ドラッグイメージを用意してImageListにする(リストビューやツリービューからDnDする場合にはListView_CreateDragImageやTreeView_CreateDragImageが便利)
  2. ImageList_BeginDragを呼ぶ
  3. DoDragDropを呼び出してドラッグを開始する
  4. IDropTarget::DragEnterで、ImageList_DragEnterを呼ぶ
  5. IDropTarget::DragOverで、ImageList_DragOverを呼ぶ
  6. IDragTarget::DragLeaveとIDragTarget::Dropで、ImageList_DragLeaveを呼ぶ
  7. DoDragDropから戻ってきたら、ImageList_EndDragを呼ぶ(必要に応じてImageList_Destroyでイメージリストを破棄する)

要するにプロセス単位(実際にはスレッド単位?)でドラッグ用のイメージをグローバルに一つ持てるようになっていてそれをソース側とターゲット側で共有していることになります。

ちなみにドラッグ中に再描画が起きた場合、そのままだとドラッグイメージが重なってしまって正しく再描画されないので、描画の前後をImageList_DragShowNolock(FALSE)とImageList_DragShowNolock(TRUE)で囲んで再描画中はドラッグイメージが隠れるようにする必要があります。ただし、ImageList_BeginDragが呼ばれた後にImageList_DragEnterが呼ばれていない状態でImageList_DragShowNolock(TRUE)が呼び出されると変なところに無理矢理ドラッグイメージが描画されてしまいますので注意が必要です。

*1  一回実装すれば使いまわせるんですが

*2  実際にはWindowsでもIE5以降が必要

*3  そもそもWindows CEにはOLE DnDがありませんけど


トップ «前の日記(2004-09-02) 最新 次の日記(2004-09-04)»