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