俺的アウトラインプロセッサーをつくる

今回はノードをドラッグ&ドロップすることで移動できるようにします。

移動前に選択されているノードを記憶しておき、移動後にもとに戻るようにしていますが、

ノードの位置を変える 俺的アウトラインプロセッサーをつくる

でもあったように、ノードを移動することで選択されているノードが意図しない変化をすることがあります。前回はフィールド変数を利用してこの問題を解決しましたが、この変数は外部からも参照されることになります。

そこで静的変数にしました。

ドロップされたときにどこに移動するかですが、

俺的アウトラインプロセッサーをつくる

で紹介したkie(Knowledge Index Editor) テキストベースのアウトラインプロセッサは、ノードのラベル部分にドロップされたときはそのノードの一番下の子、アイコン部分にドロップされたときはドロップされたノードのひとつ前の位置に移動するようにつくられています。

なぜ「前」なのか? 後ろのほうが自然な気が・・・

そこでこのサイトで作成するアウトラインプロセッサでは

ノードのラベル部分にドロップされたときはそのノードの一番下の子
アイコン部分にドロップされたときはドロップされたノードのひとつ後ろの位置

に移動するようにします。それからドラッグされているときにカーソルの形も変わるようにしてみましょう。

通常のTreeViewではドラッグされているときは、OnDragOver、ドロップされたときはOnDragDropが呼ばれますが、

俺的ライブラリをつくる 同期化されたTreeView編

で作成したクラスではTreeNodeがドラッグされているときはOnDragOverEx、TreeNodeがドロップされたときはOnDragDropExが呼ばれます。引数はDragEventExArgs型で、これを使えばどのTreeNodeがどのTreeNodeにドロップされたかがわかります。これは

ドラッグ&ドロップに対応した同期化されたTreeView

で解説しています。

このクラスを使えばTreeNodeのドラッグ&ドロップの処理は以下のようになります。

まず、ドラッグされているときにマウスオーバーされているノードが選択されるので、これによってリッチテキストの内容が変化しないようにForm1.ignoreSelectedNodeChangeをtrueにしています。Form1.ignoreSelectedNodeChange== trueのあいだは選択ノードが変化してもなにもおきません。

ドロップが開始されるときはOnBeginItemDrag、完了したときはOnEndItemDragが呼ばれるので、ここでフィールド変数の変更をおこなっています。

つぎはドラッグされているときの処理です。

まずドラッグされているとき。DragEventExArgs.HitTestLocationを調べればドラッグ元、ドロップ先だけでなく、マウスポインタがラベル部分にあるか、アイコン部分にあるかもわかります。この情報をフィールド変数inOnImageに保存しています。

またCtrlキーがおされているときは移動ではなくコピーをします。

ドロップされるとOnDragDropExが呼ばれます。

Ctrlキーが押されているのであれば移動ではなくコピー、アイコンの上にドロップされたのであればそのノードのひとつ後ろの位置に移動(またはコピー)しています。

以下はドラッグされているときにアイコンを変える処理をしています。

このようなアイコンをつくっています。それを使えるようにSyncTreeViewクラスのコンストラクタのなかで以下のような処理をしています。

そこでSyncTreeViewを継承しているSyncTreeViewExでも使用できます。

ドラッグされているときのマウスカーソルの形状変更の処理はOnGiveFeedback内でおこないます。

GiveFeedbackEventArgs.Effectの状態とフィールド変数inOnImageの状態からドロップされたらどうするのかに対応したマウスカーソルの形状に変更しています。マウスカーソルの形状を変更するときはgfbevent.UseDefaultCursorsをfalseにする必要があります。