ご存知の方、力を貸してください。
アプリをタスクトレイ付近に配置しているのですが、Comboboxを配置し
検索で絞りこんだものをドロップダウンリストに表示するようにしています。
ただ、検索した数が少ないと下に表示されComboboxの下方向に配置している
Editが隠れてしまいます。
これを隠れないように常に上方にドロップダウンリストを表示させたいのですが
何か手はないでしょうか?
多分、リストが画面からはみ出てしまう場合に上方に表示されるような仕組みと
なっているのだと思いますが、それをComboboxの位置を常に誤魔化してあげるような
処理をするとできるのではないか?と思ったりするのですが・・・
ネットで同じ質問をしているサイトを発見しますが解決までに至っていない場合が
多いようでDelphiで方法がありますでしょうか?
環境は、Delphi7を使用しています。
よろしくお願い致します。
コンボボックスがドロップダウンされた時に
リスト部分のハンドルを捕まえて、無理矢理移動することは可能です。
タイミングによっては、一瞬だけリストが下に見えてしまいますけど。
注意する点としては
・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;
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.
tor さん、monna さん
実際にコードを載せていただきありがとうございます。
結構大変なんですね。
コードの詳細な説明やDelphi用に書き換えていただいて感謝します!
自分だけで使うのであればいいのですが、職場全体で使ってもらおうと
思っているので動作がいろいろと気になってはしまうのですがお二人の
コードの動作確認と参考にいろいろ調べておりますが、普通に動かすのは
なかなか難しいようですね^^;
意図的に改行にて行数を増やしても空白部分が表示されてしまいますし、
もう少し探してみたいと思います。
解決策が見つからなければお二人のコードを参考にさせていただくと思います。
ソフトそのものはほとんど出来上がっているので後もう一歩というところで
大きな山にぶつかってしまいました。
さらに参考になるものがあればここに載せようと思うのでここはこのままにして
いろいろしてみたいと思います。
ありがとうございます。
動作確認はDelphi2009+Vistaです、
なにか不具合ありましたか?
>意図的に改行にて行数を増やしても空白部分が表示されてしまいます
コードで示して頂ければ調査します。
monaa さん ありがとうございます。
D7にて不具合は出ず、動作確認する事ができました。
これまでにすでに標準のComboboxで実装しているので置き換えるのが
ちょっと面倒だなと感じたのとリストがComboboxを中心にして下から
アニメーション表示されず標準のものと動きが違うなと感じたので
どの方法を選択すべきか検討させていただいています。
そうですね、アニメーションは上から下向ききになります。
ここを変更するのは以前検索しても出てこなかったのでパラメータの変更で
なんとかなる問題ではなさそうです。
不具合がなければ私のサポートはここまでです。
これ以上は未開の領域です。
私なら自前でコントロールを作ります。
ツイート | ![]() |