2018年11月12日月曜日

wpf : 画像キャプチャーなどに使う枠の楽な作り方

デスクトップの動画や画像をキャプチャーするときに、その範囲をマウスで指定できるツールがあります。 その範囲指定の枠をwpfで楽に作るサンプルです。 あまり試してないけど、そこそこ良さそうな出来でした。 ここにメモを残しておきます。

このページを参考にしました。

範囲選択用の枠はこんな感じです。

赤白の点線にしました。 こうすると背景が真っ白でも真っ赤でもどちらかの色は見えます。 左上の赤丸をドラッグすると枠全体を移動させることができます。 枠と赤丸は半透明にして、下が透けるようにしています。

xamlのコードはこちら。 見づらかったらテキストエディタなどにコピー&ペーストして見てください。

見て分かる通り、枠無しwindowを使っています。 Windowクラスのプロパティ(ShowInTaskbar、WindowStyle、AllowsTransparency、Background、Topmost)に必要な設定をします。 xamlにはウィンドウサイズを変更するための機能について書きません。

Windowに載っているのはBorderに囲まれたCanvasだけです。 BorderはVisualBrushで描きます。 完全に透明な部分にはマウスイベントは起きません。 このサンプルでは赤白のどちらかがあるので大丈夫ですが、例えば点線にすると線が途切れた部分はマウスでつかめないので操作しづらくなってしまいます。 そういう場合は十分な太さで非常に小さなOpacityを持つ枠を重ねて、その部分をつかめるようにします。

ペアになるxaml.csのコードはこうなります。

赤丸の部分で左クリックされたらDragMoveでWindowの位置を動かせるようにしています。 その部分は基本的な事柄なのでそのまんまです。

Loadedイベントでウィンドウプロシージャをフックして、Closingイベントで外しています。 (フックするのはSourceInitializedイベントでもよかったかも?) って事で使うのはWin32SDKでプログラムを書いていた時代のテクニックです。

WM_NCHITTESTメッセージを処理することで、ウィンドウの好きな領域を枠扱いにすることができます。 lParamにスクリーン上での座標が格納されているので取り出して、ウィンドウ上の座標に変換します。 その座標が枠の左上の角だったらHTTOPLEFTを、右上の角だったらHTTOPRIGHTなどというように元から用意されている値を返します。 そうするとデフォルトのウィンドウで角や枠をつかんだ場合などの処理が勝手に動きます。 枠以外の領域(赤丸の部分やその他の部分)はhandled = falseにしてメソッドを終えることで本来の処理に任せます。

HTTOPLEFTなどの定数はWin32SDK用のヘッダファイルに書かれていた値を手書きします。 こんな感じです。

このやり方の便利なところは、角や枠をドラッグするときマウスカーソルが勝手にウィンドウサイズ変更時のものに変わってくれることですね。 「全画面を透明なウィンドウで覆ってドラッグでRectangleを……」などと自前でコードを書くと細かい部分が大変ですが、これだと書かなくて済みます。

一応注意点もあります。 このやり方、というかwpfでキャプチャー用の枠を書くときに注意しなければならないのは論理座標と実際の座標のズレです。 ディスプレイのDPIをいじっていなければ問題ないハズですが、「字が小さくて読めな~い!!」などとDPIを変更されると論理座標と実際の座標がズレてしまいます。 そうすると枠がにじんでしまうんですよね。 そのせいで枠の内側をキャプチャー範囲にすると、そのにじみをキャプチャーしてしまう可能性があります。 そのため、「枠はキャプチャー中ずっと表示するのではなく範囲を決めるときだけ表示する」などの作りにする必要があります。

使用感については、まだあまり触ってないので分かりません。 今作っている自分用ツールができたら、ある程度使って確認しようと思います。