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

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

CButtonとCMFCButtonの比較

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

Visual Studio 2008 SP1にて導入された、MFC Feature Pack付属のCMFCButtonは、従来のCButtonに比べてかなり強力です。微妙に使い勝手が違う部分もありますが、ボタンの文字色変更とか、画像とテキスト両方表示したいときとかに、わざわざオーナードローなんかしたくないって人は重宝するんじゃないかと。
とはいえ、.NETのWindows FormsやWPFに比べたら、それでも面倒なことには変わりないです。もしどうしてもMFCを使わないといけない理由がない限り、今後はWPFを使うべきでしょう。

とりあえずサンプルを下記に置いておきます。

CButton、CMFCButtonともに、XPとVista/7、そしてビジュアル スタイルとクラシック スタイルで外見が大きく異なるボタンがあることに注意。違いを生み出しているのはコモンコントロールのバージョン(クラシックはVer.5.x、XPのビジュアルはVer.6.0、Vista/7のビジュアルはVer.6.1)ですが、このあたりはGUI開発者にとってかなり悩ましいところです。BS_BITMAPを適用して(BitmapプロパティがTrue)、WS_EX_STATICEDGEを適用して(Static EdgeプロパティがTrue)、CButton::SetBitmap()でビットマップを割り当てたボタンに至っては、なんかもう別物(;´д`)っていうか相当残念な感じになってます。

あと、以前から気になっていたんですが、チェックボックスラジオボタンをプッシュボタン形式にしてビジュアル スタイルを適用すると、かなり表示がキモいです。プッシュボタン形式というのは、BS_PUSHLIKEが適用された状態(Push LikeプロパティがTrue)のことです。Windowsのボタンはマウスでクリックしたまま離さないでいると、押し込んでいる状態を表す感じで外観が変わりますが、あのステートを使ってチェック状態を表してるつもりの変なヤツです。トグルボタンとも呼ばれます。
さらに、ビジュアル スタイルでチェックON状態のプッシュボタン形式チェックボックスにマウスオーバーすると、キモさ倍増です。自分はWindowsのクラシック スタイルが大嫌いなので、XP/Vistaでも常にビジュアル スタイルを適用していますが、いくらなんでもこれだけはクラシック スタイルのほうがON/OFFの状態が分かりやすい。サンプルではフォントを変えたり色を変えたりして、少しでも分かりやすくしようと努めてますけど、無駄な努力っていうか見苦しいだけ。MSはXPやVistaのリリース前に、一般ユーザーにビジュアル スタイルのUIをテストしてもらわなかったんでしょうか? コンシューマーゲームとかだったら、UIとか操作性は必ず一般ユーザーの反応をチェックすると思うんですが。

以下でもXP Visual Styleのトグルボタンにてホバーしたときの外観の問題点について述べられています。ちなみにtable内のspan要素のborderスタイルのoutset/insetによってunselected/selectedの外観が比較されていますが、Firefoxでは正しく表示されないようです。
devblogs.microsoft.com

なので、自分はプッシュボタン形式のチェックボックスラジオボタンはお勧めしません。「どうしても昔ながらのプッシュボタンがいいんだよ!」って方は、SetWindowTheme() APIで意図的にビジュアル スタイルを切る、という方法もありますが……みんなが慣れているであろう普通のチェックボックスラジオボタンにしておいたほうがいいと思います。

ちなみにCMFCButtonのフォントはデフォルトで必ずMS UI Gothicになるようです。親ダイアログのフォントをメイリオとかMeiryo UIとかにしても連動してくれません。親ダイアログのフォントに合わせたかったら、OnInitDialog()とかでSetFont()を呼んで明示的に設定してやる必要があります。

なお、CMFCButton::EnableWindowsTheming()は再描画メッセージを発行しません。なので、呼び出したあとはクライアント全体あるいはアプリのフレーム全体をInvalidateして再描画する必要があります。それにしても、なんでstaticメソッドなんでしょうか……個別のコントロールごとに呼べません。CMFCButtonの実装を見る限り、個別設定するならばpublicメンバー変数(orz*1)のm_bDontUseWinXPTheme(orz*2)にむりやりTRUEを設定すればいいみたいです。

スクリーンショット1(XP、ビジュアル スタイル)
https://sygh-jp.github.io/content_hosting/my_program_ss/mfc_btn_test_ss_xp01.png

スクリーンショット2(XP、クラシック スタイル)
https://sygh-jp.github.io/content_hosting/my_program_ss/mfc_btn_test_ss_xp02.png

スクリーンショット3(Vista/7、ビジュアル スタイル)
https://sygh-jp.github.io/content_hosting/my_program_ss/mfc_btn_test_ss_seven01.png

スクリーンショット4(Vista/7、クラシック スタイル)
https://sygh-jp.github.io/content_hosting/my_program_ss/mfc_btn_test_ss_seven02.png

*1:残念ながらもう慣れましたが、MFCカプセル化の設計が根本的におかしいです。フィールドをpublic/protectedで公開するとか平気でやらかしてくれていますが、どこの素人プログラマーですか……

*2:否定形の名前を変数名に付けるなと言いたいです。m_disablesVisualStyleもしくはm_usesClassicStyleにするべきでしょう。