ComboBoxの高さを変えるには

解決


risa  2010-01-11 03:34:42  No: 36922

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

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-11 03:37:42  No: 36923

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

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-11 03:43:30  No: 36924

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


tor  2010-01-11 03:58:31  No: 36925

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

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


risa  2010-01-11 04:20:02  No: 36926

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

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

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

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


risa  2010-01-18 06:41:11  No: 36927

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


Mr.XRAY  2010-01-18 09:30:14  No: 36928

こんにちは.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 09:31:07  No: 36929

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

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


Mr.XRAY  2010-01-18 09:33:55  No: 36930

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


Mr.XRAY  2010-01-18 09:49:47  No: 36931

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

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


deldel  2010-01-18 18:32:48  No: 36932

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

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


tor  2010-01-19 01:59:36  No: 36933

やや脱線しますが、コンボボックスでは本来、リストの動作を指定するスタイルとオーナー描画を指定するスタイルは独立しています。
・リストの動作: 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-19 04:16:11  No: 36934

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

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


risa  2010-01-21 06:34:46  No: 36935

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

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

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


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加