Can I programmatically set the position of the combobox drop-down list?

The usual Windows ComboBox style ( csDropDown or csDropDownList ) will open its drop-down list at the bottom right or, if there is no space below, above the combo. Can I control the position of this list (at least in Y coordinate)?

+5
source share
2 answers

Posting example code that will display the animation drop-down list correctly and make the drop-down list appear above ComboBox1 . this subclass of ComboBox hwndList code:

 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:

  • I totally agree with David and others that it is a bad idea to change this particular default behavior for TComboBox . The OP has not yet answered why he wanted this behavior.
  • The above code has been tested with D5 / XP.
+10
source

Well, you can do this using GetComboBoxInfo to get the window handle used for the list, and then move that window. Like this:

 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; 

I ignored the error checking problem to keep the code simple.

However, it should be warned that this looks pretty terrible because the animation of the drop-down menu is shown. Perhaps you can find a way to disable this.

However, you simply do not need to do anything, because Windows is already doing this for you. Drag the shape to the bottom of the screen and lower the combo. Then you will see a list appear above the combo. Like this:

enter image description here

+4
source

Source: https://habr.com/ru/post/988343/


All Articles