Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.2k views
in Technique[技术] by (71.8m points)

delphi - Can I programmatically set the position of ComboBox dropdown list?

Ordinary Windows ComboBox (csDropDown or csDropDownList style) will open its dropdown list right below or, if no space left below, above the combo. Can I control the position of this list (at least by Y coordinate)?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Posting a code example that will show drop-down list animation correctly and will force showing the drop-down list above ComboBox1. this code subclasses ComboBox hwndList:

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;

Notes:

  1. I totally agree with David, and others that this is a bad idea to change this specific default behavior for TComboBox. OP did not yet respond to why he wanted such behavior.
  2. The code above was tested with D5/XP.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...