ComboBox Windows thông thường (csDropDown
hoặc csDropDownList
kiểu) sẽ mở danh sách thả xuống của nó ngay bên dưới hoặc, nếu không có khoảng trống bên dưới, phía trên kết hợp. Tôi có thể kiểm soát vị trí của danh sách này (ít nhất theo tọa độ Y) không?Tôi có thể lập trình thiết lập vị trí danh sách thả xuống của ComboBox không?
Trả lời
Đăng một ví dụ mã mà sẽ hiển thị danh sách phim hoạt hình thả xuống một cách chính xác và sẽ buộc hiển thị danh sách thả xuống ở trên ComboBox1
. mã này lớp con 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;
Ghi chú:
- Tôi hoàn toàn đồng ý với David, và những người khác rằng đây là một ý tưởng tồi để thay đổi hành vi mặc định cụ thể này cho
TComboBox
. OP chưa trả lời lý do tại sao ông ấy muốn có hành vi như vậy. - Mã ở trên đã được thử nghiệm với D5/XP.
Đã thử nghiệm thành công, cảm ơn bạn! – Andrew
Vâng, bạn có thể thực hiện việc này bằng cách sử dụng GetComboBoxInfo
để có được xử lý đối với cửa sổ được sử dụng cho danh sách và sau đó di chuyển cửa sổ đó. Như thế nà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;
Tôi đã bỏ qua vấn đề kiểm tra lỗi để giữ mã đơn giản.
Tuy nhiên, được cảnh báo rằng có vẻ khá khủng khiếp vì hoạt ảnh thả xuống vẫn được hiển thị. Có lẽ bạn có thể tìm cách để vô hiệu hóa điều đó.
Tuy nhiên, bạn chỉ đơn giản là không cần phải làm bất cứ điều gì như thế này bởi vì Windows đã làm điều đó cho bạn. Kéo biểu mẫu xuống cuối màn hình và thả xuống combo của bạn. Sau đó, bạn sẽ thấy danh sách xuất hiện phía trên combo. Như thế này:
Được thử nghiệm trong XP với D5. mã này không hoạt động đối với tôi. 'cbi.hwndList' không được di chuyển. nó mở và đóng ngay lập tức. – kobik
@kobik Tuy nhiên, một lý do khác không làm điều này. Tôi hy vọng vấn đề là với XP chứ không phải là D5. Bạn có thể cần phải chuyển đổi hành vi cho các phiên bản hệ điều hành khác nhau. Không bao giờ là một kế hoạch tốt. –
Tôi đồng ý 100%. điều này có lẽ có thể được thực hiện bằng cách gắn kết với 'GWL_WNDPROC' và xử lý' WM_SIZE', nhưng hành vi này quá bất ngờ đến nỗi tôi hoàn toàn sẽ bỏ ý tưởng này. chỉ là một bình luận bên, tôi nghĩ rằng bằng cách sử dụng 'GetComboBoxInfo' là tốt hơn so với CB_GETCOMBOBOXINFO (xem bình luận liên quan đến tai nạn trên msdn). – kobik
Chỉ cần tự hỏi: tại sao? Điều gì trong hành vi mặc định không theo ý thích của bạn? –
@MarjanVenema Nhà thiết kế của chúng tôi muốn cải thiện khả năng sử dụng cho combobox của chủ sở hữu – Andrew