Comctrl32 ver6.0でのコンボボックスオーナー描画

[戻る]
銀の皿 2010/01/18(月) 12:28:20
環境
VC2008 SP1, WindowsVista SP1
にて開発を行っております。

ComCtrl32.dllのVer6.0(場所により6.1という表記の場合もありますが,6.0の間違い?)を
リンクして、コンボボックスを表示させるとWindowsXPやWindowsVista、7で表示されるよ
うな
リッチな見た目な(真四角で無い)コンボボックスが表示されると思います。
このコントロールは「ドロップダウンリスト」か「ドロップダウン」かにより見た目が変
わり
「ドロップダウン」の時はエディットが付属しているかのような、「ドロップダウンリス
ト」の場合はボタンであるかのような見た目になると思います。


これを例えばダイアログに配置して「ドロップダウンリスト」の「オーナー描画」と設定
をすると
いかにも「ドロップダウン」であるかのような、エディット入力可能かの様な見た目に
なってしまいます。

私としては「ドロップダウンリスト」の「オーナー描画」時は
エディット入力不可能な、ボタンな見た目にしたいのですが、OSとしての縛りなんでしょ
うか...?

何か情報のお持ちの方いらっしゃいましたら、ご教授くださいませ。

以上です。

瀬戸っぷ [E-Mail] 2010/01/19(火) 00:17:50
>これを例えばダイアログに配置して「ドロップダウンリスト」の「オーナー描画」と設定
>をすると
>いかにも「ドロップダウン」であるかのような、エディット入力可能かの様な見た目に
>なってしまいます。

オーナー描画なのですから、あなたがそのようなコードを書いたのではありませんか?
オーナー描画でないとビジュアルスタイルで描画されるのに…
ということであれば、オーナー描画の時もビジュアルスタイル相当のコントロール描画を
行えばよいのではないでしょうか?

やっことはありません(常にクラシックスタイルなので)が、過去ログから…
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200411/04110073.txt

仲澤@失業者 2010/01/19(火) 10:31:10
>オーナー描画なのですから、あなたがそのようなコードを書いたのではありませんか?

オーナードローできるのはリストの中身だけなので
外観が変わっちゃうのはなんか別の原因な匂いがします。
spy++で両者のスタイルフラグの違いでも見てみたらどうでしょう。
今Vistaのテストができないのでそれ以上なんとも・・・(vv;)。

銀の皿 2010/01/19(火) 11:35:16
>瀬戸っぷ 様
ご返事有難うございます。

>オーナー描画なのですから、あなたがそのようなコードを書いたのではありませんか?
1.VC2008にてダイアログを扱うプロジェクトを生成。
2.コンボボックスを配置。
3.プロパティで、「ドロップダウンリスト」と「Owner Draw=固定」に。(「変数」でも今
回の動作に違いはありませんでした)

リソースエディタ上ではボタンlikeな「ドロップダウンリスト」の見た目ですが
実行するとエディットボックスlikeな「ドロップダウン」の見た目になってしまいます。
勿論「Owner Draw=なし」にしてみると、ボタンlikeな「ドロップダウンリスト」になり
ます。

DrawItemメソッドの実装をしていないから、エディットボックスlikeになるのかも。とも
思いましたが
コンボボックスの右端にあるボタン部分は勝手にアニメーションします。

DrawItemのフラグを見て、エディットボックスに相当する部分を描画する際に
渡されてくるrectを見ると、右端のボタンを除いた領域が渡されていますし
クライアント領域でDrawThemeBackGroundを行ってもボタンは表示されっぱなしなので
DrawItemの後にOS?MFC?によって実行される後処理がある様な気がします。


よって、今の所
1.オーナー描画で描けるのは、エディットボックスに相当する部分と(ドロップダウンす
る箇所をリストボックスと呼ぶとして)リストボックスの各行だけである。
2.オーナー描画の場合は須らく、「ドロップダウン」の見た目で扱われる。
 なぜならエディットボックスに相当する部分の背景を描く際に
 白背景ならば自分の好きな描画をしても違和感は無いが
 「ドロップダウンリスト」の様に、グラデーションを行っている箇所に背景を描画する
と違和感満載だから。

という風に解釈しています。


もう少し調査を行おうと思っていますので、こちらの方も未解決とさせて頂こうと思いま
す。
瀬戸っぷ様、ありがとうございました。

銀の皿 2010/01/19(火) 11:48:41
>仲澤@失業者 様
ご返事有難うございます。

spy++では両者の違いは、CBS_OWNERDRAW???????の違いだけでした。
(場合によりCBS_HASSTRINGの有り無しがありますが、rcファイルに後付しても同じでした)

>オーナードローできるのはリストの中身だけなので
という事であるならば、私が上のレスで書いた
>白背景ならば自分の好きな描画をしても違和感は無いが
>「ドロップダウンリスト」の様に、グラデーションを行っている箇所に背景を描画すると
違和感満載だから。
よって、グラデーション描画を行っている「ドロップダウンリスト」形式の描画は
オーナー描画時は我慢してね。という事なのかもしれません。

仲澤@失業者様、ありがとうございました。

PATIO 2010/01/19(火) 13:21:42
オーナードロウにするとクラシックにしていても通常のコンボボックスとは
若干違った表示になります。
他の人のPCで確認は指定無いので環境依存はあるかもしれません。
いずれにしてもオーナー描画を行う為の仕掛けと画面テーマ等を反映する
ロジックとの間に衝突なり、うまく行かない要素があってオーナー描画の時は
一番最初の頃の最もベーシックなスタイルに戻っているのかもしれません。

どうもOSというかAPIレベルで同居できない事情があるようなので
諦めるしかないかもしれませんね。

銀の皿 2010/01/20(水) 10:41:51
>PATIO 様
ご返事有難うございます。
終電間際に見たもので、御礼が遅くなりました。
おはようございます。

>オーナードロウにするとクラシックにしていても通常のコンボボックスとは
>若干違った表示になります。
なるほど。

やはりとといいますか、諦める所なのかもしれません。


遅くなりましたが、こちらが第一にしたい事は
単純に「コンボボックスに表示される文字列を上下中央揃えしたい」だけなのです。
コンボボックスを指定の高さで表示する事は、CComboBoxのSetItemHeightにて実現できま
したが
このままでは表示される文字列が左上固定で出てしまいます。

最終的な逃げは、「ドロップダウン」「ドロップダウンリスト」の区別はつかないものの
オーナードローでCDC::DrawTextなどで上下中央揃えにして逃げようと思っています。

ただ、後学のためにと「非オーナードローでCComboBox::OnPaintを全て自分でコーディン
グ」
という方法を試しています。
コンボボックス本体はこれで無事に描画できましたが、ドロップダウンされる
CListBox(?)のをサブクラス化しても自作クラスのOnPaintが実行されません。
親コンボボックスがオーナードローで無いので、DrawItemが呼ばれないのは当たり前で
しょうが
OnPaintが呼ばれないのは何故?と困っています。
自作CListBoxを普通にダイアログに貼った時は普通にOnPaintされますので
「CListBoxの親(オーナー?)が受け取っている」とかそういった理由かなぁ、と考えてい
ます。

もし、上記の件詳しい方がいらっしゃいましたらご教授くださいませ。

以上。

仲澤@失業者 2010/01/20(水) 14:04:28
まず、コンボボックスは複合ウインドウです。
ドロップしていないウインドウが、常に表示されており、
選択された項目が表示されています。「コンボメインWin」としておきます。

次に、ドロップされた場合にはメニュー状の項目選択ウインドウが
Createされて表示されます。これを「ドロップWin」とします。
オーナードローの対象とされているのは「ドロップWin」です。
また、「ドロップWin」はアクティブでなくなると即座にDestroyされて
しまいます。

さて、CComboBoxをサブクラス化したときにはどちらのウインドウの
コールバックが差し替えられているのでしょうか。
試していないですが、「コンボメインWin」側ではないでしょうか。

最後に、外観の描画がOnPaint()で行われるかどうかは、コントロール系
のウインドウでは微妙です。これらには
 1.クライアントしかないもの
 2.非クライアントしかないもの
 3.両方持つもの
がありえます。また、3.の場合でも、非クライアントを含めて全描画
してしまう場合にはWM_PAINTではなくWM_NCPAINTで全て描画を完了してしまい
ValidateWindow()してしまうやつもいます。結果WM_PAINTはいっさい発行
されません。

参考になれば、以上を勘案案してみてはどうでしょう。

銀の皿 2010/01/20(水) 16:22:13
>仲澤@失業者 様
度々有難うございます。
本当に助かっています。

>さて、CComboBoxをサブクラス化したときにはどちらのウインドウの
>コールバックが差し替えられているのでしょうか。
>試していないですが、「コンボメインWin」側ではないでしょうか。
これについては、以下の様に実装していますので「ドロップWin」側をサブクラス化出来
ていると思います。
1. rcに貼ったコンボボックスをCMyComboBoxにサブクラス化。
2. CMyComboBoxのPreSubclassWindowにて、GetComboBoxInfoのhwndListを
   CMyListBoxInComboにサブクラス化。
   (GetClassNameで見てみるとhwndListは"ComboLBox"となっています)

調査を進めて、CMyComboBoxのOnDropDown時にもGetComboBoxInfoのhwndListを見ると
先ほどの[2.]の時に確認したHWNDになっています。

ただ、これでは仲澤さんの仰る
>また、「ドロップWin」はアクティブでなくなると即座にDestroyされて
>しまいます。
には、そぐわないので何かもう一つウィンドウがある気がしてきました・・・。
無理やりCMyListBoxInComboのOnPaint関数自体を呼んでみて描画させても
選択行と非選択行で処理が分かれている様な感じです。


WM_PAINT, WM_NCPAINTの件、なるほど。です。
特に
>また、3.の場合でも、非クライアントを含めて全描画
>してしまう場合にはWM_PAINTではなくWM_NCPAINTで全て描画を完了してしまい
>ValidateWindow()してしまうやつもいます。結果WM_PAINTはいっさい発行
>されません。
凄く勉強になりました、有難うございます。

もう少しデバッグを続けてみます。

銀の皿 2010/01/22(金) 13:24:18
[[解決]]
こんにちは、やりたい事が実現できましたので報告にあがりました。

もともとやりたかったのは「コンボボックスを指定の高さで、且つ表示される文字は上下
中央揃えで表示」でした。(それ以外もありますが)

そして、それを実現するためにオーナードローを用いろうとしましたが
Vista以降(XPからでしたっけ?)のコンボボックスでは、オーナードロー属性のついた
「ドロップダウンリスト」Comboは「ドロップダウン」Comboの様な見た目になってしまう
という問題がありました。

よって、以下の様な案にしました。
まず今回のクラスをCMyComboBoxとして
1.CMyComboBoxを使用したいダイアログで宣言、DDX_Controlを用いてサブクラス化する。
2.CMyComboBoxには以下のコードを実装する。
  (今回のクラスを直接Createする事は考えていません)

a. CMyComboBox::PreSubclassWindowをオーバーライド
   ModifyStyle(CBS_OWNERDRAWFIXED, 0 , SWP_FRAMECHANGED)にて動的にオーナードロー
属性を付加。

b. CMyComboBox::OnPaintにて、コンボボックスを描画する自作メソッドDrawComboBoxを
コール。
   DrawComboBoxはDrawThemeBackgroundを用いコンボボックスの見た目を実現する。
   現在選択されているテキストの描画もここで行う。

c. CMyComboBox::DrawItemにて、DRAWITEMSTRUCTのitemActionのODA_FOCUSが立っている
ならば
   再度DrawComboBoxをコールする。
   その後、リストボックスを描画する自作メソッドDrawListBoxをコール。
   DrawListBoxはハイライト行の描画やテキストの描画を行う。

この3つを実装して、今のところ実現しています。
この方法は一旦試していたのですが、DRAWITEMSTRUCTのitemActionのODA_FOCUSをチェッ
クしていなかったため
フォーカス移動時に一瞬白くなるタイミングがあり、無理なのかなぁと思っていました。

まとまりなく長文になりましたがご容赦ください。


瀬戸っぷ 様
仲澤@失業者 様
PATIO 様
 ありがとうございました。

仲澤@失業者 2010/01/22(金) 18:33:10
コンボボックス系のコントロールはまだ改善の余地のありそう
(色選択とか)なので、結果報告は大変参考になります。
個人的には、こうしてくれる方が増えてくれるととうれしいですね。

銀の皿 2010/01/22(金) 20:52:19
追記です。

ModifyStyle(CBS_OWNERDRAWFIXED, 0 , SWP_FRAMECHANGED)
と書いていましたが、これを実行しても実際にはオーナードローにはなりません。
エディットのES_MULTILINEも同じくModifyStyleやSetWindowLongでは無理ですが
コードに書いてみて「お、出来てるじゃん!」と思ってたら
実際にはリソースで設定してました…、トホホ。

結果として、今回はリソースで指定する様にしました。

エディットにES_MULTILINEを指定する時はPreSubclassWindowで
必要な属性を付加した同じウィンドウを生成してアタッチ。
さらにGetSuperWndProcAddr(だったかな?)をオーバーライド...等、無理くりやったので
すが
コンボは複雑な上、使用箇所が少なかったのでリソースで対応しました。


>仲澤@失業者 様
確かにもっと改良の余地はいっぱいあると思います。
Win7でもっとしっかりと変わってくれる事を祈っています。
皆さん自分の作業に忙しくて此処の事を忘れちゃってますね…。
ご助言ありがとうございました。

古いスレッドには追加発言できません。