Delphi 我可以通过编程设置组合框下拉列表的位置吗?

Delphi 我可以通过编程设置组合框下拉列表的位置吗?,delphi,combobox,drop-down-menu,position,Delphi,Combobox,Drop Down Menu,Position,普通Windows组合框(csDropDown或csDropDownList样式)将在组合框的正下方打开其下拉列表,如果下面没有空格,则在组合框的上方打开。我是否可以控制此列表的位置(至少通过Y坐标) 好的,您可以通过使用获取用于列表的窗口的句柄,然后移动该窗口来完成此操作。像这样: type TMyForm = class(TForm) ComboBox1: TComboBox; procedure ComboBox1DropDown(Sender: TObject);

普通Windows组合框(
csDropDown
csDropDownList
样式)将在组合框的正下方打开其下拉列表,如果下面没有空格,则在组合框的上方打开。我是否可以控制此列表的位置(至少通过Y坐标)

好的,您可以通过使用获取用于列表的窗口的句柄,然后移动该窗口来完成此操作。像这样:

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已经为您做了。将表单拖到屏幕底部,然后下拉组合框。然后您将看到列表显示在组合上方。像这样:

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;

发布一个代码示例,该示例将正确显示下拉列表动画,并将强制显示上面的下拉列表
ComboBox1
。此代码将组合框
hwnlist
子类化:

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;

注意事项:

  • 我完全同意David和其他人的观点,即改变
    TComboBox
    的这种特定默认行为是个坏主意。OP还没有回答为什么他想要这样的行为
  • 上面的代码是用D5/XP测试的

  • 只是想知道:为什么?默认行为中有什么不符合您的喜好?@MarjanVenema我们的设计师想对owner draw comboboxTested在XP中使用D5进行一些可用性改进。这个代码对我不起作用。未移动
    cbi.hwdlist
    。它会立即打开和关闭。@kobik还有另一个不这样做的原因。我认为问题在于XP而不是D5。您可能需要切换不同操作系统版本的行为。从来都不是一个好计划,我百分之百同意。这可能是通过连接到
    GWL\u WNDPROC
    并处理
    WM\u SIZE
    来实现的,但是这种行为是如此出乎意料,以至于我完全放弃了这个想法。只是一个旁白,我认为使用
    GetComboBoxInfo
    比CB_getComboxInfo(参见msdn上关于崩溃的评论)要好。@kobik谢谢,我已经将它切换到getComboxInfo了