WPF 4.6 での修正点、透明な子ウィンドウや High DPI 改善について

Japan C# Users Group の Visual Studio 2015 リリース記念勉強会 にて、.NET Framework 4.6 で WPF に加わった修正点について発表しました。 大きな変更はなく、細かい機能追加やバグ修正程度なのですが、2つ程ピックアップして紹介しています。

Transparent Child Window support

WPF の子ウィンドウが透過できるようになります。

そもそも子ウィンドウをどういうときに使うのか… というと、Win32 相互運用シナリオや、MDI を実現したいケースでしょうか。

Win32 相互運用、つまり WPF ウィンドウ内に HwndHost (WebBrowser や WindowsForms コントロール、Win32 要素など) を表示するとき、MSDN にも記述がある通り、様々な制約があります。 特に、「HwndHost は、同じトップレベル ウィンドウの他の WPF 要素の上に表示されます。」というのが曲者で、ZIndex 等を一切無視し、HwndHost が一番上に表示されるため、その上に WPF 要素を表示することはできません。

Browser (HwndHost) は必ず全要素の上に描画されてしまう

ウィンドウ ハンドルを 1 つしか持たない WPF アプリと、それぞれの要素がウィンドウ ハンドルを持つ GDI 等の旧来のテクノロジーは、まさに水と油のような関係であり、仕組み上仕方ないことではあります。 そこで、WebBrowser や過去の資産を HwndHost 上で表示し、さらにその上に WPF 要素を表示したいとき、独立したウィンドウ ハンドルを持つ子ウィンドウはその解決方法のひとつとなります (他には、System.Windows.Controls.Primitives.Popup などが使えたり)。

(ちなみに Win32 相互運用についてはいくらかノウハウを持っているのですが、個人的にこれ以降使う気がしないので、ブログにまとめていません… もし知りたい方がいらっしゃれば @Grabacr07 に圧力をかけまくるとそのうち書き始めるかもしれません…)

また、WPF は MDI をサポートしていません。 個人的には MDI は好みではない (自宅も職場もトリプル モニター、というマルチ モニター厨なので相性が悪い) ものの、もし実装する場合は、子ウィンドウが役に立つかもしれません。

子ウィンドウを作るコードの例を示します。 一般的な WPF アプリ開発において HwndSourceParameters を使って HwndSource を自作するようなシナリオは殆どなさそうなので、見慣れないかもしれません。

サンプル コード全体は後ほどアップデートするかも。

実行して「Create child」ボタンを押すと、このような MDI 子ウィンドウっぽいものが表示されます。 .NET Framework 4.5.2 またはそれ以前をターゲットにしているアプリでは、17 行目の Background で Alpha に 128 等を指定して透過させようとしても、透明になりません (黒くなるだけ)。

MDI のようなもの (非透過)

そこで、アプリケーションのターゲット フレームワークを .NET Framework 4.6 にした上で、HwndSourceParameters のオブジェクト初期化子に以下を追加します。 また、背景を Alpha に 128 を指定して子ウィンドウのルート要素を透過させてみます。

実行すると以下のように。 子ウィンドウが透過できているのが判るかと思います。

透過した子ウィンドウ

この HwndSourceParameters.UsesPerPixelTransparency プロパティが、.NET Framework 4.6 で追加されたメンバーです。 true を指定すると、上記のように WPF 子ウィンドウが透過できるようになります。

これは、WebBrowser 等の HwndSource に対しても、透過したうえでその上に表示することができます。 よって、先述の Win32 相互運用シナリオにおける HwndHost の前後関係の問題にも寄与できるでしょう。

注意しなければならないのが、この機能を使うためには、アプリケーション マニフェストで Windows 8 向けのビルドであることを明示する必要があるということです。

app.manifest を作ると、上記のようなコードが最初から記述されているはずです。 ここで、Windows 8 のコメントを外すだけです。

余談ですが、app.manifest での指定をせずに UsesPerPixelTransparency=”True” を使用すると、Win32Exception がスローされて表示できません。

High DPI Improvements

Hight DPI 環境下におけるレイアウトの丸め処理が改善されました。

元々どんな問題があったかというと、私の MetroRadiance がモロにその影響を受けていて、こちらの記事 で指摘して頂いています。 この場合のレイアウト システムの詳細な動作については把握できていませんが、1 dip (デバイス非依存ピクセル) を指定したとき、DPI 150 % 環境下で Round(1 * 1.5) = 2 になるはずが、1 になることがあるようです。

.NET Framework 4.6 では、このような BorderThickness や Margin のレイアウトの丸め処理が改善されており、ズバり上記のような問題が発生しなくなりました。 1 枚目が .NET Framework 4.5.2 での実行結果、2 枚目が 4.6 での実行結果です。

Bottom だけ 1px になってる。超ダサい。 すべて 2px に。キレイ。

この改善は、.NET Framework 4.6 をターゲットにしたアプリであれば、特にコードの修正等は必要なく、既定で適用されます (要はバグ修正的なノリなんですかね)。 また、.NET Framework 4.5.2 またはそれ以前をターゲットにしたアプリでも、.NET Framework 4.6 がインストールされている環境であれば、app.config に以下のようなコードを記述することで、この改善を受けることができます。

Switch.MS.Internal.DoNotApplyLayoutRoundingToMarginsAndBorderThickness (長ぇ)。

そして、High DPI に関しては、上記の改善のほかに “Multi-DPI setup” 環境下での改善も入っているようです (Per-Monitor DPI のこと?)。 ブラックアウトする問題が修正されているそうですが、そういう事象に遭遇したことがないので、よくわからず…。 調査中。

Visual Studio 2015 での新機能

WPF 開発で使える Visual Studio 2015 の新機能をちょこっと紹介。

Peek Definition と CodeLens はセッション資料にある通りです。 Live VisualTree と Live Property Explorer は、アプリ実行中に VisualTree の参照や各要素のプロパティ値の修正を可能にするものです。 Web 開発におけるブラウザーの開発者ツールのような感覚です。

実行中にガイドを表示しながら要素を選択して…

ガイドを表示しながら要素を選択(画像クリックで GIF アニメーション)

Live VisualTree で要素を選択したりデザイナー上に一発で呼びだしたり…

要素選んだりデザイナーで表示したり(画像クリックで GIF アニメーション)

Live Property Explorer で実行中にプロパティを書き替えたり。

プロパティを書き替え(画像クリックで GIF アニメーション)

すばらすぃ。 アプリのデバッグ効率が飛躍的に向上します。 まじ最強。

まとめ

という感じでした。 他にもタッチ サポートが改善されたりしているようですが、バグ修正っぽいのでスルーしています。

何か判ったことがあれば追記します。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です