ComboBox di Windows ordinario (stile csDropDown
o csDropDownList
) aprirà il suo elenco a discesa subito sotto o, se non vi è spazio disponibile in basso, sopra la combo. Posso controllare la posizione di questo elenco (almeno per la coordinata Y)?Posso impostare a livello di codice la posizione dell'elenco a discesa ComboBox?
risposta
pubblicazione di un esempio di codice che mostra la lista di animazione discesa correttamente e costringerà che mostra l'elenco a discesa sopra ComboBox1
. questo codice sottoclassi 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;
Note:
- Sono totalmente d'accordo con David, e altri che questa è una cattiva idea quella di modificare questo comportamento predefinito specifico per
TComboBox
. OP non ha ancora risposto a perché voleva tale comportamento. - Il codice sopra è stato testato con D5/XP.
Testato con successo, grazie! – Andrew
Bene, è possibile farlo utilizzando GetComboBoxInfo
per ottenere un handle per la finestra utilizzata per l'elenco e quindi spostare quella finestra. Come questo:
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;
ho ignorato la questione del controllo degli errori per mantenere il codice semplice.
Tuttavia, si tenga presente che sembra piuttosto orribile perché l'animazione a discesa è ancora visualizzata. Forse puoi trovare un modo per disabilitarlo.
Tuttavia, non è necessario fare nulla di simile perché Windows lo fa già per te. Trascina un modulo nella parte inferiore dello schermo e trascina verso il basso la tua combo. Quindi vedrai apparire la lista sopra la combo. Come questo:
Testata in XP con D5. questo codice non funziona per me. il 'cbi.hwndList' non viene spostato. si apre e si chiude immediatamente. – kobik
@kobik Un altro motivo per non farlo. Mi aspetto che il problema sia con XP piuttosto che con D5. Probabilmente è necessario cambiare il comportamento per diverse versioni del sistema operativo. Mai un buon piano. –
Sono d'accordo al 100%. questo potrebbe probabilmente essere fatto agganciandolo a 'GWL_WNDPROC' e gestendo' WM_SIZE', ma il comportamento è così inaspettato che vorrei scaricare completamente questa idea. solo un commento a parte, penso che l'uso di 'GetComboBoxInfo' sia migliore di CB_GETCOMBOBOXINFO (vedi il commento relativo agli arresti anomali su msdn). – kobik
Basta chiedersi: perché? Cosa nel comportamento predefinito non è di tuo gradimento? –
@ MarjanVenema Il nostro progettista desidera migliorare l'usabilità per la casella combinata owner-draw – Andrew