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

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

Win32/MFCとWinForms/WPF相互運用

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

以前からWin32/MFCWPFの相互運用性(主にWPF 3.5 *1)に関して独自調査を続けているんですが、メッセージまわりでやはり問題がいくつか発生することが判明してきています。基本的にはWin32/MFCがホストとなる場合、C++/CLIコードにてHwndSourceを、逆にWPFがホストとなる場合ではHwndHostを使えばわりと簡単に相互運用できるんですが、キーボード イベントまわりでHwndSourceHookIKeyboardInputSinkあたりを使ってごりごりコードを書かないといけない場面があります。これを怠ると文字の直接入力、タブ キーによるフォーカス移動やアクセス キーが効かないだけじゃなく、WindowsFormsHostを使ったWPFコントロールをHwndSourceでホストしてるときにメインスレッドがハングすることすらあります(例えばWindowsFormsHost内のコントロールにフォーカスがあるときにモーダル メッセージ ボックスを出すとハングする)。MFC + WPFとかWPF + WinFormsでは顕現しなかった不具合が、MFC + WPF + WinFormsで発覚することがあって、これが厄介きわまりない。しかもWinFormsはMFCほどひどくないにしても、WPFほど統一的にクラス設計されてるわけじゃないので、単純にWindowsFormsHost経由で透過的に解決できそうにない感じ。WPFが良い技術なのは確かなんですが、すべてのコードをWPFに移行できるわけじゃないし、C#すら習得してない人も多いから、過渡期では相互運用がいかに易しくできるか、ってのがすごく大切だと思うんですが、現状では相互運用をするときはC++/CLIという.NET界でも最も変態的な言語を介してマニアックなグルーコードを書かざるをえません(C++/CLIをどうしても使いたくないのであれば、WPFを使ったアセンブリをCOMコンポーネントとして公開する方法もありますが、逆にC++/CLIを使う場合と比べてかえってモジュールやABI境界の管理が激しく面倒になります)。

なお、Extended WPF ToolkitなるものがMS Public Licenseで公開されているので、WindowsFormsHostは使わずにできるかぎりコレを活用したほうがよさそうです。個人的にWinFormsでかなり便利だったNumericUpDownがWPFで改めて実装されています(2012年6月現時点での最新版の1.6.0ではNumericUpDownがObsolete扱いになり、DecimalUpDown, DoubleUpDown, IntegerUpDownに分割されてるらしい)。ですがタッチUI環境では、テキストボックスの右端や左端に付けられた小さなスピンコントロールは操作しづらいので、結局WPF標準のRepeatButtonを使って自前でClickイベントのコードビハインドもしくはコマンドバインディングを書くようにしたほうがかえって良いかもしれません。

ちなみにWPF on Win32/MFCだと、FocusVisualStyleが効かなくなる現象が発生します(Win32/MFCウィンドウ上に配置したWPF User Control内のUI要素にフォーカスしても点線のフォーカス枠線が表示されないし、カスタマイズも効かない)。ボタンやエディットボックスは枠線がなくても外観が変わるのでフォーカスされてることが分かるんですが、チェックボックスやらラジオボタンやらドロップダウン リスト形式コンボボックス(Vista/7)やらではフォーカスが分からなくなるので、フォーカス状態に応じて背景ブラシを変更するなりしたほうがいいかもです。それにしても何でBorderは点線スタイル系のプロパティ(DashStyleとかStrokeDashArrayとか)を既定でサポートしてくれてないんでしょうか……WPFではカスタム コントロールの作成やレンダリングのカスタマイズ、依存関係プロパティの定義自体は(Win32オーナードローと比べて)簡単だから別にいいっちゃいいんですけどね……

*1:WPF 4の使えるVisual Studio 2010環境では、C++/CLIのインテリセンスが効かないのでスキップしました。Visual Studio 2012/2013ではインテリセンスが復活しています。