2012-02-06 13 views
5

通常のWindowsコンボボックス(csDropDownまたはcsDropDownListスタイル)は、その下にドロップダウンリストを表示します。このリストの位置を(少なくともY座標で)制御できますか?ComboBoxドロップダウンリストの位置をプログラムで設定できますか?

+6

ちょっと疑問:なぜですか?どのようなデフォルトの動作では、あなたの好みにはありません? –

+0

@ MarjanVenema私たちのデザイナーはオーナードローコンボボックスで使いやすさを改善したいと思っています – Andrew

答えて

10

が正しくドロップダウンリストのアニメーションが表示され、ComboBox1の上にドロップダウンリストを示す強制するコードの例を投稿:このように。

TForm1 = class(TForm) 
    ComboBox1: TComboBox; 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
private 
    FComboBoxListDropDown: Boolean; 
    FComboBoxListWnd: HWND; 
    FOldComboBoxListWndProc, FNewComboBoxListWndProc: Pointer; 
    procedure ComboBoxListWndProc(var Message: TMessage); 
end; 

.... 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    Info: TComboBoxInfo; 
begin 
    ZeroMemory(@Info, SizeOf(Info)); 
    Info.cbSize := SizeOf(Info); 
    GetComboBoxInfo(ComboBox1.Handle, Info); 
    FComboBoxListWnd := Info.hwndList; 
    FNewComboBoxListWndProc := MakeObjectInstance(ComboBoxListWndProc); 
    FOldComboBoxListWndProc := Pointer(GetWindowLong(FComboBoxListWnd, GWL_WNDPROC)); 
    SetWindowLong(FComboBoxListWnd, GWL_WNDPROC, Integer(FNewComboBoxListWndProc)); 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    SetWindowLong(FComboBoxListWnd, GWL_WNDPROC, Integer(FOldComboBoxListWndProc)); 
    FreeObjectInstance(FNewComboBoxListWndProc); 
end; 

procedure TForm1.ComboBoxListWndProc(var Message: TMessage); 
var 
    R: TRect; 
    DY: Integer; 
begin 
    if (Message.Msg = WM_MOVE) and not FComboBoxListDropDown then 
    begin 
    FComboBoxListDropDown := True; 
    try 
     GetWindowRect(FComboBoxListWnd, R); 
     DY := (R.Bottom - R.Top) + ComboBox1.Height + 1; 
     // set new Y position for drop-down list: always above ComboBox1 
     SetWindowPos(FComboBoxListWnd, 0, R.Left, R.Top - DY , 0, 0, 
     SWP_NOOWNERZORDER or SWP_NOZORDER or SWP_NOSIZE or SWP_NOSENDCHANGING); 
    finally 
     FComboBoxListDropDown := False; 
    end; 
    end; 
    Message.Result := CallWindowProc(FOldComboBoxListWndProc, 
    FComboBoxListWnd, Message.Msg, Message.WParam, Message.LParam); 
end; 

注:このコードは、コンボボックスhwndListをサブクラス

  1. 私は完全にこれはTComboBoxのために、この特定のデフォルト動作を変更することは悪い考えであることをデビッドに同意する、など。 OPはまだに反応していませんでした。理由はです。
  2. 上記のコードはD5/XPでテストしました。
+0

正常にテストされました、ありがとう! – Andrew

4

これを行うには、GetComboBoxInfoを使用して、リストに使用されているウィンドウのハンドルを取得し、そのウィンドウを移動します。このように:

type 
    TMyForm = class(TForm) 
    ComboBox1: TComboBox; 
    procedure ComboBox1DropDown(Sender: TObject); 
    protected 
    procedure WMMoveListWindow(var Message: TMessage); message WM_MOVELISTWINDOW; 
    end; 

.... 

procedure TMyForm.ComboBox1DropDown(Sender: TObject); 
begin 
    PostMessage(Handle, WM_MOVELISTWINDOW, 0, 0); 
end; 

procedure TMyForm.WMMoveListWindow(var Message: TMessage); 
var 
    cbi: TComboBoxInfo; 
    Rect: TRect; 
    NewTop: Integer; 
begin 
    cbi.cbSize := SizeOf(cbi); 
    GetComboBoxInfo(ComboBox1.Handle, cbi); 
    GetWindowRect(cbi.hwndList, Rect); 
    NewTop := ClientToScreen(Point(0, ComboBox1.Top-Rect.Height)).Y; 
    MoveWindow(cbi.hwndList, Rect.Left, NewTop, Rect.Width, Rect.Height, True); 
end; 

私はコードを単純にするためにエラーチェックの問題を無視しました。

ただし、ドロップダウンアニメーションがまだ表示されているので、かなり恐ろしいことに注意してください。おそらく、それを無効にする方法を見つけることができます。

しかし、Windowsは既にあなたのためにこのようなことを行う必要はありません。フォームを画面の下にドラッグしてコンボをドロップします。次に、コンボの上にリストが表示されます。

enter image description here

+3

D5でXPでテストしました。このコードは私のために働いていません。 'cbi.hwndList'は移動されていません。すぐに開閉します。 – kobik

+1

@kobikこれをやっていないもう一つの理由。問題はD5ではなくXPであると思います。異なるOSバージョンの動作を切り替える必要があるかもしれません。決して良い計画ではありません。 –

+2

私は100%同意します。これはおそらく 'GWL_WNDPROC'にフックして' WM_SIZE'を処理することによって行うことができますが、その動作は予期せぬものであり、私はこの考えを完全にダンプします。ちょうどサイドのコメント、私は 'GetComboBoxInfo'を使用してCB_GETCOMBOBOXINFO(msdnのクラッシュに関するコメントを参照)よりも優れていると思う。 – kobik

関連する問題