ComboBoxの高さを変えるには

解決


risa  2010-01-10 18:34:42  No: 36922  IP: 192.*.*.*

いつもお世話になります。以下の事項について質問させてください。

ComboBoxの高さを高くしようと、3箇所に1行ずつコードを書きました。
概ね意図したとおりに動くのですが、コンボボックスの隣にある下向きの
三角の部分を押すと、その三角の部分の描画がおかしくなってしまいます。
これを綺麗に描画するにはどうすればよいでしょうか?
よろしくお願いします。(Delphi6を使用しています。)

procedure TForm1.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
begin
  SendMessage(ComboBox1.Handle, CB_SETITEMHEIGHT, -1, 16);
end;

procedure TForm1.ComboBox1CloseUp(Sender: TObject);
begin
  SendMessage(ComboBox1.Handle, CB_SETITEMHEIGHT, -1, 16);
end;

procedure TForm1.ComboBox1Click(Sender: TObject);
begin
  SendMessage(ComboBox1.Handle, CB_SETITEMHEIGHT, -1, 16);
end;

編集 削除
risa  2010-01-10 18:37:42  No: 36923  IP: 192.*.*.*

念のためもうひとつ。
全項目の高さも統一して変更すれば、意図したとおりに動くのでは?と考え、
次のコードも試してみましたが、やはりうまくいきませんでした。

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  // コンボボックスの全項目の高さを高くする。
  for i := - 1 to ComboBox1.Items.Count - 1 do
    SendMessage(ComboBox1.Handle, CB_SETITEMHEIGHT, i, 16)
end;

編集 削除
KHE00221  2010-01-10 18:43:30  No: 36924  IP: 192.*.*.*

Fontサイズ変えれば自動的に高さも変わると思うけど?

編集 削除
tor  2010-01-10 18:58:31  No: 36925  IP: 192.*.*.*

コンボボックスはスタイルによって挙動が違いますが、使っているStyleは何でしょう?
StyleをcsOwnerDrawFixedにして、ItemHeightを使うのでは駄目なんでしょうか?
(OnDrawItemで自分で描画しなくちゃいけなくなりますけど)

ちなみに、CB_SETITEMHEIGHTも、オーナードローでない場合は
wParamに-1と0以外の値は指定できないとあります。

編集 削除
risa  2010-01-10 19:20:02  No: 36926  IP: 192.*.*.*

ご回答、どうもありがとうございます。

それで、Fontのサイズの件についてなのですが、先方から指摘されたサイズ
がありますので、それを遵守しなくてはなりません。今からの変更もちょっ
と難しいです。

それと、スタイルはcsDropDown(初期値)を使用しています。自由に入力
できる必要がある項目なので、これを使用しています。

wParamに-1 と0以外を指定できないことは勉強になりました。
一見利いていたように見えたのですが、無効な処理だったのですね。
お客さんのプログラムにみっともない処理をはさむところでした。ご指摘いただいて、本当に助かりました。m(__)m

編集 削除
risa  2010-01-17 21:41:11  No: 36927  IP: 192.*.*.*

やはり高さを変えることは難しいでしょうか?
いろいろ調べたりしていますが、まだできていません。
もし分かる方がいらっしゃれば、ぜひお願いいたします。

編集 削除
Mr.XRAY  2010-01-18 00:30:14  No: 36928  IP: 192.*.*.*

こんにちは.Mr.XRAYです.

だいぶ日にちが経過しているので,ネット等で調査済みだとは思いますが,
(それはやってみましたが... ということになるのかも知れませんが..)

OwnerDrawではどうですか.
ただし,動作確認は Delphi6(UP2) + Window XP(SP3)でXPテーマ無使用です.
XPテーマを有効にするとどうなるかは不明です.

また,どうしてもcsDropDownという場合はできないと思いますよ.
だって,Draw(描画)を変更しないんですから.
(Itemの)の高さを変えると言うことは,描画を変えるということですから.

//=============================================================================
//  ComboBoxでオーナードロウする
//  StyleプロパティをcsOwnerDrawFixedかcsOwnerDrawVariableにしないと無効
//  ComboBoxの高さはItemHeightで変更する
//=============================================================================
procedure TForm1.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
begin
   TComboBox(Control).Canvas.FillRect(Rect);

   if odFocused in State then
   begin
      TComboBox(Control).Canvas.Font.Color := clWhite;
   end else
   begin
      TComboBox(Control).Canvas.Font.Color := clWindowText;
   end;
   //2や5の値はItemHeightよよっても体裁が変わるので適時変更する
   //あるいは自動計算のコードを書く
   TComboBox(Control).Canvas.TextOut(Rect.Left+2,
                                     Rect.Top+5,
                                     TComboBox(Control).Items[Index]);

end;

編集 削除
tor  2010-01-18 00:31:07  No: 36929  IP: 192.*.*.*

正当な手段はオーナードローにしてItemHeightを指定するなり
OnMeasureItemを処理することなんですけど、
それだと動作としてはcsDropDownList相当になるから駄目ということですね。

TComboBoxExを使って、Heightを目的の高さに設定したImageListを関連づけるとそれっぽくなります。これではどうでしょうか?
(DropDownCountとの関係がちょっと怪しくなるので調整がいりそうですが)

編集 削除
Mr.XRAY  2010-01-18 00:33:55  No: 36930  IP: 192.*.*.*

失礼しました.
この方法は既にtorさんがレスしていましたね.
Styleを変更したくないとのことですので,私のレスは忘れてください.

編集 削除
Mr.XRAY  2010-01-18 00:49:47  No: 36931  IP: 192.*.*.*

>Styleを変更したくないとのことですので,私のレスは忘れてください.

ただちょっと一言.
目的で達成できるのであれば,変更してもいいと思うんですが.

編集 削除
deldel  2010-01-18 09:32:48  No: 36932  IP: 192.*.*.*

背景の色が同じパネルを用意して、
その中にコンボボックスを置いて、
FormCreateなどでコンボボックスの枠を
消すというのはどうでしょうか?

  SetWindowRgn(ComboBox1.Handle, CreateRectRgn(2,2,ComboBox1.Width - 2, ComboBox1.Height - 2), True);

編集 削除
tor  2010-01-18 16:59:36  No: 36933  IP: 192.*.*.*

やや脱線しますが、コンボボックスでは本来、リストの動作を指定するスタイルとオーナー描画を指定するスタイルは独立しています。
・リストの動作: CBS_SIMPLE, CBS_DROPDOWN, CBS_DROPDOWNLIST
・オーナー描画: なし、CBS_OWNERDRAWFIXED, CBS_OWNERDRAWVARIABLE

本来なら3×3=9通りの組み合わせがあるはずなのですが、VCLではこれを一つのStyleに押し込めて5通りに制限してしまっています。
なので、リストの動作はcsSimpleまたはcsDropDownと同等で、かつオーナードローを有効にするということができないんですね。

そこで、ウィンドウスタイルを勝手にいじって、csDropDownの動作とオーナー描画を両立できないか試してみました。
カスタムコンポーネントを作ることになってしまいますけれど、まあ参考までに…

type
  TMyComboBox = class(TComboBox)
  public
    procedure CreateParams(var Params: TCreateParams); override;
  end;

procedure TMyComboBox.CreateParams(var Params: TCreateParams);
begin
  inherited;
  // デフォルトではCBS_DROPDOWNが設定されているはずなので
  // これにオーナー描画スタイルを追加する
  Params.Style := Params.Style or CBS_OWNERDRAWFIXED;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ComboBox1 := TMyComboBox.Create(Self);
  ComboBox1.Parent := Self;
  ComboBox1.ItemHeight := 適当な高さ;
end;

高さを変えるだけなら、特にOnMeasureItemやOnDrawItemを実装しなくてもこれだけで動いてくれるようです。
ちなみに、こちらの方法だと文字列がボックスの上に張り付いたイメージになりますが、
先ほど提案したTComboBoxExだと逆に下に張り付いたようになりますね。

編集 削除
risa  2010-01-18 19:16:11  No: 36934  IP: 192.*.*.*

一晩立ったらこんなにレスが…
みなさん、本当にありがとうございます。つまらない質問に、こんなにも
いろいろと考えてくださって、本当に頭の下がる思いです。

今は出先なので、まだ確認できませんが、帰ったらご教示いただいた方法
をそれぞれ確認させていただきたいと思います。結果報告も必ずいたします
ので、もう少しお時間をください。

編集 削除
risa  2010-01-20 21:34:46  No: 36935  IP: 192.*.*.*

今、動作確認を行いました。結論から申し上げますと、最終的には
tor様の最後のご提案部分を利用させていただき、実現できました。
ほんの些細な変更だったのですが、画面上でコントロールを美しく
配置できるようになり、先方からも非常に喜ばれました。本当にあり
がとうございます。外国のサイトまで検索しても見つからず、ここ1ヶ
月以上悩んでいた案件でしたので、本当にうれしいです。

>deldel様
そのサンプル自体は入手していたのですが、後ろにパネルを置く、など
の対案までは思いつかなかったので、結局見送っていました。でも、今
やってみたら、私の要件を満たす見栄えを実現できそうな方法ですね。
今後、他の同様の場面で困ったときなど、そのアイデアを流用させて
いただきたいと思います。

>Mr.XRAY様
あなたのサンプルはいつも参考にさせていただいております。今回も
非常に多くの情報をご提示いただきまして、本当にありがとうございます。
この場をお借りして、日ごろの感謝もこめてお礼を申し上げます。

編集 削除