2012-02-06 36 views
5

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?

+6

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? –

+0

@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

Trả lời

10

Đă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ú:

  1. 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.
  2. Mã ở trên đã được thử nghiệm với D5/XP.
+0

Đã thử nghiệm thành công, cảm ơn bạn! – Andrew

4

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:

enter image description here

+3

Đượ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

+1

@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. –

+2

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

Các vấn đề liên quan