Il componente TComboBox combina una casella di modifica con un elenco "pick" scorrevole. Gli utenti possono selezionare un elemento dall'elenco o digitare direttamente nella casella di modifica.
Quando una casella combinata si trova nello stato a discesa Windows disegna un tipo di controllo casella di riepilogo per visualizzare gli elementi della casella combinata per la selezione.
Il Proprietà DropDownCount specifica il numero massimo di elementi visualizzati nell'elenco a discesa.
Il larghezza dell'elenco a discesa sarebbe, per impostazione predefinita, uguale alla larghezza della casella combinata.
Quando la lunghezza (di una stringa) degli elementi supera la larghezza della casella combinata, gli elementi vengono visualizzati come troncati!
TComboBox non fornisce un modo per impostare la larghezza del suo elenco a discesa :(
È possibile impostare la larghezza dell'elenco a discesa inviando un messaggio speciale di Windows alla casella combinata. Il messaggio è CB_SETDROPPEDWIDTH e invia la larghezza minima consentita, in pixel, della casella di riepilogo di una casella combinata.
Per codificare le dimensioni dell'elenco a discesa su, diciamo, 200 pixel, è possibile eseguire:
SendMessage (theComboBox.Handle, CB_SETDROPPEDWIDTH, 200, 0);
Questo va bene solo se sei sicuro che tutti i tuoi theComboBox.Items non sono più lunghi di 200 px (quando disegnati).
Per essere sicuri che l'elenco a discesa sia sempre sufficientemente largo, possiamo calcolare la larghezza richiesta.
Ecco una funzione per ottenere la larghezza richiesta dell'elenco a discesa e impostarla:
procedura ComboBox_AutoWidth (const theComboBox: TCombobox); const HORIZONTAL_PADDING = 4; var itemsFullWidth: intero; idx: intero; itemWidth: intero; inizio itemsFullWidth: = 0; // ottieni il massimo necessario con degli articoli nello stato a discesa per idx: = 0 per -1 + theComboBox.Items.Count fare inizio itemWidth: = theComboBox.Canvas.TextWidth (theComboBox.Items [idx]); Inc (itemWidth, 2 * HORIZONTAL_PADDING); if (itemWidth> itemsFullWidth) poi itemsFullWidth: = itemWidth; fine; // imposta la larghezza del menu a discesa, se necessario Se (itemsFullWidth> theComboBox.Width) quindi inizio // controlla se ci sarebbe una barra di scorrimento Se theComboBox.DropDownCount < theComboBox.Items.Count poi itemsFullWidth: = itemsFullWidth + GetSystemMetrics (SM_CXVSCROLL); SendMessage (theComboBox.Handle, CB_SETDROPPEDWIDTH, itemsFullWidth, 0); fine; fine;
La larghezza della stringa più lunga viene utilizzata per la larghezza dell'elenco a discesa.
Quando chiamare ComboBox_AutoWidth?
Se si precompila l'elenco di elementi (in fase di progettazione o durante la creazione del modulo) è possibile chiamare la procedura ComboBox_AutoWidth all'interno del modulo OnCreate gestore di eventi.
Se si modifica dinamicamente l'elenco degli elementi della casella combinata, è possibile chiamare la procedura ComboBox_AutoWidth all'interno del OnDropDown gestore eventi: si verifica quando l'utente apre l'elenco a discesa.
Un test
Per un test, abbiamo 3 caselle combinate su un modulo. Tutti hanno elementi con il loro testo più ampio della larghezza effettiva della casella combinata. La terza casella combinata viene posizionata vicino al bordo destro del bordo del modulo.
La proprietà Items, in questo esempio, è precompilata: chiamiamo ComboBox_AutoWidth nel gestore eventi OnCreate per il modulo:
// Form's OnCreate procedura TForm.FormCreate (Mittente: TObject); inizio ComboBox_AutoWidth (ComboBox2); ComboBox_AutoWidth (ComboBox3); fine;
Non abbiamo chiamato ComboBox_AutoWidth per Combobox1 per vedere la differenza!
Si noti che, quando eseguito, l'elenco a discesa per Combobox2 sarà più ampio di Combobox2.
Per Combobox3, quello posizionato vicino al bordo destro, l'elenco a discesa viene tagliato.
L'invio di CB_SETDROPPEDWIDTH estenderà sempre la casella di riepilogo a discesa a destra. Quando la casella combinata si trova vicino al bordo destro, l'estensione della casella di riepilogo più a destra comporterebbe l'interruzione della visualizzazione della casella di riepilogo.
Dobbiamo in qualche modo estendere la casella di riepilogo a sinistra in questo caso, non a destra!
CB_SETDROPPEDWIDTH non ha modo di specificare in quale direzione (sinistra o destra) estendere la casella di riepilogo.
Proprio quando viene visualizzato l'elenco a discesa, Windows invia il messaggio WM_CTLCOLORLISTBOX alla finestra principale di una casella di riepilogo - alla nostra casella combinata.
Essere in grado di gestire WM_CTLCOLORLISTBOX per la casella combinata sul bordo destro dovrebbe risolvere il problema.
The Onnipotente WindowProc
Ogni controllo VCL espone la proprietà WindowProc - la procedura che risponde ai messaggi inviati al controllo. È possibile utilizzare la proprietà WindowProc per sostituire o sottoclassare temporaneamente la procedura window del controllo.
Ecco il nostro WindowProc modificato per Combobox3 (quello vicino al bordo destro):
// modificato ComboBox3 WindowProc procedura TForm.ComboBox3WindowProc (var Messaggio: TMessage); var cr, lbr: TRect; inizio // disegna la casella di riepilogo con elementi combinati se Message.Msg = WM_CTLCOLORLISTBOX allora inizio GetWindowRect (ComboBox3.Handle, cr); // rettangolo casella di riepilogo GetWindowRect (Message.LParam, lbr); // spostalo a sinistra per abbinare il bordo destro Se cr.Right lbr.Right poi MoveWindow (Message.LParam, lbr.Left- (lbr.Right-clbr.Right), lbr.Top, lbr.Right-lbr.Left, lbr.Bottom-lbr.Top, True); fine altro ComboBox3WindowProcORIGINAL (Messaggio); fine;
Se il messaggio ricevuto dalla nostra casella combinata è WM_CTLCOLORLISTBOX, otteniamo il rettangolo della finestra, otteniamo anche il rettangolo della casella di riepilogo da visualizzare (GetWindowRect). Se sembra che la casella di riepilogo appaia più a destra, la spostiamo a sinistra in modo che la casella combinata e il bordo destro della casella di riepilogo siano uguali. Facile come quello :)
Se il messaggio non è WM_CTLCOLORLISTBOX, chiamiamo semplicemente la procedura di gestione dei messaggi originale per la casella combinata (ComboBox3WindowProcORIGINAL).
Infine, tutto ciò può funzionare se lo abbiamo impostato correttamente (nel gestore eventi OnCreate per il modulo):
// Form's OnCreate procedura TForm.FormCreate (Mittente: TObject); inizio ComboBox_AutoWidth (ComboBox2); ComboBox_AutoWidth (ComboBox3); // collega WindowProc modificato / personalizzato per ComboBox3 ComboBox3WindowProcORIGINAL: = ComboBox3.WindowProc; ComboBox3.WindowProc: = ComboBox3WindowProc; fine;
Dove nella dichiarazione del modulo abbiamo (intero):
genere TForm = classe(TForm) ComboBox1: TComboBox; ComboBox2: TComboBox; ComboBox3: TComboBox; procedura FormCreate (Mittente: TObject); privato ComboBox3WindowProcORIGINAL: TWndMethod; procedura ComboBox3WindowProc (var Messaggio: TMessage); pubblico Dichiarazioni pubbliche fine;
E questo è tutto. Tutto gestito :)