Zwykły styl Windows ComboBox (csDropDown
lub csDropDownList
) otworzy jego listę rozwijaną tuż poniżej lub, jeśli nie ma wolnego miejsca poniżej, nad kombinacją. Czy mogę kontrolować pozycję tej listy (przynajmniej przez współrzędną Y)?Czy mogę programowo ustawić pozycję listy rozwijanej ComboBox?
Odpowiedz
zamieszczaniu przykład kodu, który pokaże rozwijaną listę animację poprawnie i zmusi pokazujący listy rozwijanej powyżej ComboBox1
. Ten kod podklas 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;
Uwagi:
- I całkowicie zgadzam się z Dawidem, a inni, że jest to zły pomysł, aby zmienić to domyślne zachowanie specyficznego dla
TComboBox
. OP jeszcze nie odpowiedział na pytanie, dlaczego chciał takie zachowanie. - Powyższy kod został przetestowany z D5/XP.
Testowany pomyślnie, dziękuję! – Andrew
Możesz to zrobić, używając GetComboBoxInfo
, aby uzyskać uchwyt do okna użytego na liście, a następnie przenieść to okno. W ten sposób:
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;
Zignorowałem kwestię sprawdzania błędów, aby zachować prosty kod.
Należy jednak pamiętać, że wygląda to dość okropnie, ponieważ animacja rozwijana jest nadal wyświetlana. Być może znajdziesz sposób, aby to wyłączyć.
Po prostu nie trzeba robić nic takiego, ponieważ system Windows już to robi. Przeciągnij formularz na dół ekranu i upuść kombi. Następnie zobaczysz listę wyświetloną nad kombinacją. Tak:
Testowany w XP z D5. ten kod nie działa dla mnie. 'cbi.hwndList' nie jest przenoszony. natychmiast się otwiera i zamyka. – kobik
@kobik Kolejny powód, żeby tego nie robić. Oczekuję, że problem dotyczy raczej XP niż D5. Prawdopodobnie musisz zmienić zachowanie dla różnych wersji systemu operacyjnego. Nigdy dobrego planu. –
Zgadzam się w 100%. można to prawdopodobnie zrobić przez zahaczenie do 'GWL_WNDPROC' i obsługę' WM_SIZE', ale zachowanie jest tak nieoczekiwane, że całkowicie zrzuciłem ten pomysł. tylko komentarz boczny, myślę, że używając 'GetComboBoxInfo' jest lepszy niż CB_GETCOMBOBOXINFO (patrz komentarz dotyczący awarii na msdn). – kobik
Zastanawiasz się: dlaczego? Co w domyślnym zachowaniu nie jest do twoich upodobań? –
@MarjanVenema Nasz projektant chce wprowadzić pewne ulepszenia użyteczności dla właściciela-remisu combobox – Andrew