Comboboxのドロップダウンリストの方向について


きゃっと  2010-02-23 02:19:38  No: 37735

ご存知の方、力を貸してください。

アプリをタスクトレイ付近に配置しているのですが、Comboboxを配置し
検索で絞りこんだものをドロップダウンリストに表示するようにしています。

ただ、検索した数が少ないと下に表示されComboboxの下方向に配置している
Editが隠れてしまいます。
これを隠れないように常に上方にドロップダウンリストを表示させたいのですが
何か手はないでしょうか?

多分、リストが画面からはみ出てしまう場合に上方に表示されるような仕組みと
なっているのだと思いますが、それをComboboxの位置を常に誤魔化してあげるような
処理をするとできるのではないか?と思ったりするのですが・・・
ネットで同じ質問をしているサイトを発見しますが解決までに至っていない場合が
多いようでDelphiで方法がありますでしょうか?

環境は、Delphi7を使用しています。
よろしくお願い致します。


tor  2010-02-23 03:11:09  No: 37736

コンボボックスがドロップダウンされた時に
リスト部分のハンドルを捕まえて、無理矢理移動することは可能です。
タイミングによっては、一瞬だけリストが下に見えてしまいますけど。

注意する点としては
・OnDropDownは実際にリストが表示される「前」に起きるが
移動はリストが表示された「後」にやらなくてはいけない
・コンボボックス自体は子ウィンドウだけれど、
リストの部分はどのウィンドウにも属さないオーバーラップウィンドウなので
座標系が異なる。

以下実装例

const
  MSG_AFTER_DROPPED_DOWN = WM_APP + 1; // リストが表示された後に自分に送るメッセージ
type
  TForm1 = class(TForm)
    ComboBox1: TComboBox;
    procedure ComboBox1DropDown(Sender: TObject);
  private
    procedure AfterDroppedDown(var msg: TMessage); message MSG_AFTER_DROPPED_DOWN;
  end;

procedure TForm1.ComboBox1DropDown(Sender: TObject);
begin
  // この時点ではまだリストが表示されていないので
  // 後で処理されるよう、自分自身にメッセージを投げておく
  PostMessage(Handle, MSG_AFTER_DROPPED_DOWN, 0, 0);
end;

procedure TForm1.AfterDroppedDown(var msg: TMessage);
  var
    info: TComboBoxInfo;
    hList: HWND;
    rcList: TRect;
    ptEdit: TPoint;
begin
  // コンボボックスの情報を取得
  info.cbSize := SizeOf(info);
  GetComboBoxInfo(ComboBox1.Handle, info);
  // リスト部分のハンドルとサイズを取得
  hList := info.hwndList;
  GetWindowRect(hList, rcList);
  // エディット部分の座標を取得し、スクリーン座標に変換
  ptEdit := Point(info.rcItem.Left, info.rcItem.Top);
  Windows.ClientToScreen(info.hwndCombo, ptEdit);
  // 境界線の分を引いておく
  Dec(ptEdit.X, GetSystemMetrics(SM_CXEDGE));
  Dec(ptEdit.Y, GetSystemMetrics(SM_CYEDGE));
  // リストをエディット部の上に移動
  SetWindowPos(hList, 0, ptEdit.X, ptEdit.Y - rcList.Bottom + rcList.Top, 0, 0, SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE);
end;


monaa  2010-02-23 06:12:49  No: 37737

MSDNに例ありましたよ。
WM_CTLCOLORLISTBOXのタイミングたベターらしいです。
せっかくなのでDelphiで書き直しておきました。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TComboBoxEx = class(TComboBox)
  private
    FComboDefaultProc: TWndMethod;
    constructor Create(AOwner: TComponent); override;
    procedure ComboProc(var Message: TMessage);
  end;

  TForm1 = class(TForm)
  private
    { Private 宣言 }
    ComboBoxEx : TComboBoxEx;
  public
    { Public 宣言 }
    constructor Create(AOwner: TComponent); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TComboBoxEx }
//http://msdn.microsoft.com/ja-jp/library/ms996411.aspx
procedure TComboBoxEx.ComboProc(var Message: TMessage);
var
  aPos:TPoint;
const
  topMgn = 2;
begin
  if (Message.Msg = WM_CTLCOLORLISTBOX) then
  begin
    aPos := Point(Left,Top);
    aPos := ClientToScreen(aPos);
    if DropDownCount < Items.Count then
      aPos.Y := aPos.Y - (DropDownCount * ItemHeight) -topMgn else
      aPos.Y := aPos.Y - (Items.Count   * ItemHeight) -topMgn ;
    if aPos.Y >= 0 then
    SetWindowPos(Message.LParam,
              0,aPos.X,aPos.Y,0,0,
              SWP_NOSIZE);
  end;
  FComboDefaultProc(Message);
end;

constructor TComboBoxEx.Create(AOwner: TComponent);
begin
  inherited;
  FComboDefaultProc:=WindowProc;
  WindowProc := ComboProc;
end;

constructor TForm1.Create(AOwner: TComponent);
var
  i:Integer;
begin
  inherited;
  ComboBoxEx := TComboBoxEx.Create(self);
  ComboBoxEx.Parent := Self;
  for i := 0 to 10 do
    ComboBoxEx.Items.Add(IntToStr(i));
  ComboBoxEx.ItemIndex := 0;
end;

end.


きゃっと  2010-02-23 21:00:15  No: 37738

tor さん、monna さん

実際にコードを載せていただきありがとうございます。
結構大変なんですね。
コードの詳細な説明やDelphi用に書き換えていただいて感謝します!

自分だけで使うのであればいいのですが、職場全体で使ってもらおうと
思っているので動作がいろいろと気になってはしまうのですがお二人の
コードの動作確認と参考にいろいろ調べておりますが、普通に動かすのは
なかなか難しいようですね^^;

意図的に改行にて行数を増やしても空白部分が表示されてしまいますし、
もう少し探してみたいと思います。

解決策が見つからなければお二人のコードを参考にさせていただくと思います。
ソフトそのものはほとんど出来上がっているので後もう一歩というところで
大きな山にぶつかってしまいました。

さらに参考になるものがあればここに載せようと思うのでここはこのままにして
いろいろしてみたいと思います。
ありがとうございます。


monaa  2010-02-25 09:00:07  No: 37739

動作確認はDelphi2009+Vistaです、
なにか不具合ありましたか?
>意図的に改行にて行数を増やしても空白部分が表示されてしまいます
コードで示して頂ければ調査します。


きゃっと  2010-02-26 01:45:56  No: 37740

monaa さん  ありがとうございます。

D7にて不具合は出ず、動作確認する事ができました。

これまでにすでに標準のComboboxで実装しているので置き換えるのが
ちょっと面倒だなと感じたのとリストがComboboxを中心にして下から
アニメーション表示されず標準のものと動きが違うなと感じたので
どの方法を選択すべきか検討させていただいています。


monaa  2010-02-26 09:52:39  No: 37741

そうですね、アニメーションは上から下向ききになります。
ここを変更するのは以前検索しても出てこなかったのでパラメータの変更で
なんとかなる問題ではなさそうです。
不具合がなければ私のサポートはここまでです。
これ以上は未開の領域です。
私なら自前でコントロールを作ります。


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

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






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