FlowLayoutPanel での DragDrop サンプルコード

http://siokoshou.googlepages.com/

FlowLayoutPanelでのDragDrop、だいぶ前に書いてからまだだらだらやってたんですが、いい加減飽きてきました(^^;
何かの参考になることもあるかもしれないので、コードを公開します。なぜか2000行超えてます。なんでこんなにいったんだろうw MSDNのコードもご参考にどうぞ。
WindowsXPエクスプローラで縮小版表示したときのDragDrop動作をだいたい真似てます。細かいところでちょっと違うけど、だいたいあんな感じ。


サンプルを実行するとFlowLayoutPanel上にラベルが並んでて、このラベルをマウスで並べ替えできます。Ctrl押しながらDragDropでコピーです。Link動作は未サポート。2つ起動して一方から他方に移動ももちろんできます。
カーソルの移動にあわせて、Iビームがここに入るよってナビゲートします。カーソルは半透明のソースのラベル。ドロップできないところにカーソルが乗ったらStopなカーソルに変わったり、コピー動作ではコピーを示すカーソルに変わります。コピーのカーソルは今どきっぽい(?)緑のプラス( ̄ー ̄)


制限がいろいろ…。
先に書いたとおりLink未対応。カーソル描くのがめんどかったw
同時に複数のラベルを掴むのは未サポート。次の課題はこれだなぁ。
FlowDirectionはLeftToRightのみ対応。必要なかったので。RightToLeftはNoにしか対応していません。Inheritもだめ。
(11/28追記)
一番肝心な制限を書き忘れていたので追記。各子コントロールにマージンがないとIビームが描けません!サンプルはAll=10にしています。
縦1列に並んだ場合には対応しているものの、LeftToRightでありながらFlowBreakで2列以上になるような場合も未対応です。
サンプルってことでご容赦を。
(追記ここまで)
こんなところです。


エクスプローラのDragDrop動作を調べているときに気付いたけど、一覧表示と縮小版表示でDragDropの動作が違うんですね。縮小版だとアドレスバーにドロップできてしまって、バグ?とか思ったけど実にどうでもいいことなのでスルーしておきます。


ソースはDragDropSupportフォルダの下がDragDropのコア部分。それ以外はデモコードです。ライブラリ化しようと思ったけど、制限が多くてちょっと…って状況なので、デモ部分と一緒になってます。
FlowLayoutDragDropSupportクラスがターゲット側FlowLayoutPanelのDragDrop支援クラス。使い方はデモ部分のStackPanelコードを参考にどうぞ。名前長杉…
DragDropScrollはFlowLayoutPanelのDragDrop中のスクロール支援クラス。DragDrop中にカーソルが端に近づいたらスクロールします。
DragDropSourceSupportクラスがソース側支援クラス。使い方はDropSourceLabelを参考に。
IDragDropSourceもソース側で使います。Dragソースの前後にカーソルがあると、Iビームが表示されず、マウスボタンを離してもDropしません。これを実現するために、私がソースですって名乗り出るために使っています。もっとうまい方法がないものかなぁ?


ほかはライブラリ内の内部クラス(のつもり)です。
BeamクラスはIビームを描くクラスですが、IBeamにするとインターフェイスとまぎらわしいので、苦しいけどBeamの名前にしました。Beamじゃ何のことか分かりませんねw
DragDropCursorはソースコントロールを半透明カーソルにするクラス。
FlowBase、HorizontalFlow、VerticalFlow、ZeroFlowがFlowLayoutPanel上の子コントロールのレイアウトを解析して、カーソル位置から挿入位置を割り出すクラス。stateパターン。不用意にネストクラスを使ってしまったので、ちょっと分かり辛いかも。今は反省している
DragEnterで子コントロールのレイアウトをすべて解析して、各コントロールごとの領域をCellクラスに持ちます。DragOverではカーソル位置から下にあるCellを検索します。処理速度は良好っぽいです。


VisualStudioでデバッグする際、ドラッグ中にカーソルがタスクバーに乗ると「COMException がネイティブまたはマネージ境界を越えました」って出ます。これを止めるにはツール→オプションのデバッグ→全般の一番上にある「例外がAppDomainまたはマネージ/ネイティブの境界を超える場合にブレークする(マネージのみ)」のチェックを外せばOKです。このエラー、「FORMATETC 構造体が無効です (HRESULT からの例外: 0x80040064 (DV_E_FORMATETC))」ってやつなんですが、よく分かりません。


興味のある方は覗いてみてください。
#あ〜っ、ソリューション名が…名前空間が…最初に作ったもののままだったorz スルーしてください…

(11/28追記)制限に抜けがあったので、追記しました。