syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

WPFレイヤードウィンドウのヒットテストを無効化

(これは2012-08-24に書いた故OCNブログの記事を移植したものです)

WPFでレイヤード ウィンドウを作る場合、

  • WindowStyle="None"
  • Background="Transparent"
  • AllowsTransparency="True"

のように設定するだけで簡単に実現できます。ピクセル単位のアルファ透過も簡単です。ネイティブWin32のDirect2D/Direct3Dレイヤード ウィンドウを作るときは相当苦労したのに……

で、レイヤード ウィンドウのヒット テストを無効化したいとき、ルート要素(Window)のIsHitTestVisibleをFalseにしただけでは無効になりません。P/Invokeを使って、GetWindowLong()/SetWindowLong() Win32 APIによりネイティブの拡張ウィンドウ スタイルWS_EX_TRANSPARENTを設定してやる必要があります。詳しくはココWPFのWindowは、ButtonとかのVisual要素とは違って、内部でWin32 API(User32/GDI32)を使って実装されてるっていう証拠ですね。他にも、WindowStyleにNoneを指定すると、ウィンドウを最大化・最小化するときなどのアニメーションがなくなってしまうのですが、これは同様にWS_CAPTIONスタイルを指定することで一部のアニメーションを復活させることができるようです。

WPFに限らず、レイヤード ウィンドウを使うとHUD (Head-up Display) が簡単かつきれいに作れそう。なお、一時的に表示をON/OFFしたいときは、Visibilityの制御ではなくOpacityをいじったほうがよさそうです(Visibilityを操作すると、Windowのアクティブ状態が変動してしまう)。

余談:64bit対応の話

64bit版Windows (Win64) では、プラットフォームに応じてサイズが変動するポインタなどの情報をやりとりする場合は、GetWindowLong()/SetWindowLong()ではなく、戻り値および引数にLONG_PTRが使われているGetWindowLongPtr()/SetWindowLongPtr()を使います。
しかし、ウィンドウ スタイル系の情報 (GWL_STYLE, GWL_EXSTYLE) は32bit幅までしか使われないため、Win64においてもGetWindowLong()/SetWindowLong()で十分なようです。
なお、GetWindowLong()/SetWindowLong()はWin32/Win64ともに関数エントリポイントの実体が存在しますが、Win32でのGetWindowLongPtr()/SetWindowLongPtr()はマクロにより定義されており、GetWindowLong()/SetWindowLong()に置き換わるだけです。Win64では実体が存在します。P/Invokeするときは注意が必要です。