Le composant TComboBox combine une zone d'édition avec une liste déroulante de «sélection». Les utilisateurs peuvent sélectionner un élément dans la liste ou taper directement dans la zone d'édition.
Lorsqu'une zone de liste déroulante est dans un état déroulant Windows dessine un type de contrôle de zone de liste pour afficher les éléments de la zone de liste déroulante pour la sélection.
le Propriété DropDownCount spécifie le nombre maximal d'éléments affichés dans la liste déroulante.
le largeur de la liste déroulante serait, par défaut, égal à la largeur de la zone de liste déroulante.
Lorsque la longueur (d'une chaîne) d'éléments dépasse la largeur de la zone de liste déroulante, les éléments s'affichent comme tronqués!
TComboBox ne fournit pas un moyen de définir la largeur de sa liste déroulante :(
Nous pouvons définir la largeur de la liste déroulante en envoyant un message Windows spécial à la zone de liste déroulante. Le message est CB_SETDROPPEDWIDTH et envoie la largeur minimale autorisée, en pixels, de la zone de liste d'une zone de liste déroulante.
Pour coder en dur la taille de la liste déroulante à, disons, 200 pixels, vous pouvez faire:
SendMessage (theComboBox.Handle, CB_SETDROPPEDWIDTH, 200, 0);
Ce n'est ok que si vous êtes sûr que tous vos éléments theComboBox ne dépassent pas 200 px (lorsqu'ils sont dessinés).
Pour nous assurer que la liste déroulante est toujours suffisamment large, nous pouvons calculer la largeur requise.
Voici une fonction pour obtenir la largeur requise de la liste déroulante et la définir:
procédure ComboBox_AutoWidth (const theComboBox: TCombobox); const HORIZONTAL_PADDING = 4; var itemsFullWidth: entier; idx: entier; itemWidth: entier; commencer itemsFullWidth: = 0; // obtenir le maximum nécessaire avec des éléments dans l'état déroulant pour idx: = 0 à -1 + theComboBox.Items.Count faire commencer itemWidth: = theComboBox.Canvas.TextWidth (theComboBox.Items [idx]); Inc (itemWidth, 2 * HORIZONTAL_PADDING); if (itemWidth> itemsFullWidth) ensuite itemsFullWidth: = itemWidth; fin; // définir la largeur de la liste déroulante si nécessaire si (itemsFullWidth> theComboBox.Width) puis commencer // vérifier s'il y aurait une barre de défilement si theComboBox.DropDownCount < theComboBox.Items.Count ensuite itemsFullWidth: = itemsFullWidth + GetSystemMetrics (SM_CXVSCROLL); SendMessage (theComboBox.Handle, CB_SETDROPPEDWIDTH, itemsFullWidth, 0); fin; fin;
La largeur de la chaîne la plus longue est utilisée pour la largeur de la liste déroulante.
Quand appeler ComboBox_AutoWidth?
Si vous pré-remplissez la liste des éléments (au moment de la conception ou lors de la création du formulaire), vous pouvez appeler la procédure ComboBox_AutoWidth dans le formulaire OnCreate gestionnaire d'événements.
Si vous modifiez dynamiquement la liste des éléments de la zone de liste déroulante, vous pouvez appeler la procédure ComboBox_AutoWidth dans le OnDropDown gestionnaire d'événements - se produit lorsque l'utilisateur ouvre la liste déroulante.
Un examen
Pour un test, nous avons 3 zones de liste déroulante sur un formulaire. Tous ont des éléments dont le texte est plus large que la largeur réelle de la zone de liste déroulante. La troisième zone de liste déroulante est placée près du bord droit de la bordure du formulaire.
La propriété Items, pour cet exemple, est pré-remplie - nous appelons notre ComboBox_AutoWidth dans le gestionnaire d'événement OnCreate pour le formulaire:
// OnCreate du formulaire procédure TForm.FormCreate (expéditeur: TObject); commencer ComboBox_AutoWidth (ComboBox2); ComboBox_AutoWidth (ComboBox3); fin;
Nous n'avons pas appelé ComboBox_AutoWidth pour Combobox1 pour voir la différence!
Notez que, lors de l'exécution, la liste déroulante pour Combobox2 sera plus large que Combobox2.
Pour Combobox3, celle placée près du bord droit, la liste déroulante est coupée.
L'envoi de CB_SETDROPPEDWIDTH étendra toujours la zone de liste déroulante vers la droite. Lorsque votre zone de liste déroulante est près du bord droit, étendre la zone de liste plus à droite entraînerait la coupure de l'affichage de la zone de liste.
Nous devons en quelque sorte étendre la zone de liste vers la gauche lorsque c'est le cas, pas vers la droite!
Le CB_SETDROPPEDWIDTH n'a aucun moyen de spécifier dans quelle direction (gauche ou droite) étendre la zone de liste.
Juste au moment où la liste déroulante doit être affichée, Windows envoie le message WM_CTLCOLORLISTBOX à la fenêtre parent d'une zone de liste - à notre zone de liste déroulante.
La possibilité de gérer WM_CTLCOLORLISTBOX pour la zone de liste déroulante proche du bord droit résoudrait le problème.
La fenêtre toute-puissante
Chaque contrôle VCL expose la propriété WindowProc - la procédure qui répond aux messages envoyés au contrôle. Nous pouvons utiliser la propriété WindowProc pour remplacer ou sous-classer temporairement la procédure de fenêtre du contrôle.
Voici notre WindowProc modifié pour Combobox3 (celui près du bord droit):
// ComboBox3 WindowProc modifié procédure TForm.ComboBox3WindowProc (var Message: TMessage); var cr, lbr: TRect; commencer // dessiner la zone de liste avec les éléments de la zone de liste déroulante si Message.Msg = WM_CTLCOLORLISTBOX alors commencer GetWindowRect (ComboBox3.Handle, cr); // rectangle de list box GetWindowRect (Message.LParam, lbr); // le déplacer vers la gauche pour correspondre à la bordure droite si cr.Right lbr.Right ensuite MoveWindow (Message.LParam, lbr.Left- (lbr.Right-clbr.Right), lbr.Top, lbr.Right-lbr.Left, lbr.Bottom-lbr.Top, True); fin autre ComboBox3WindowProcORIGINAL (Message); fin;
Si le message que notre zone de liste déroulante reçoit est WM_CTLCOLORLISTBOX, nous obtenons le rectangle de sa fenêtre, nous obtenons également le rectangle de la zone de liste à afficher (GetWindowRect). S'il apparaît que la zone de liste apparaît plus à droite - nous la déplaçons vers la gauche pour que la zone de liste déroulante et la bordure droite de la zone de liste soient identiques. Aussi simple que ça :)
Si le message n'est pas WM_CTLCOLORLISTBOX, nous appelons simplement la procédure de gestion des messages d'origine pour la zone de liste déroulante (ComboBox3WindowProcORIGINAL).
Enfin, tout cela peut fonctionner si nous l'avons correctement défini (dans le gestionnaire d'événement OnCreate du formulaire):
// OnCreate du formulaire procédure TForm.FormCreate (expéditeur: TObject); commencer ComboBox_AutoWidth (ComboBox2); ComboBox_AutoWidth (ComboBox3); // attacher WindowProc modifié / personnalisé pour ComboBox3 ComboBox3WindowProcORIGINAL: = ComboBox3.WindowProc; ComboBox3.WindowProc: = ComboBox3WindowProc; fin;
Où, dans la déclaration du formulaire, nous avons (entier):
type TForm = classe(TForm) ComboBox1: TComboBox; ComboBox2: TComboBox; ComboBox3: TComboBox; procédure FormCreate (expéditeur: TObject); privé ComboBox3WindowProcORIGINAL: TWndMethod; procédure ComboBox3WindowProc (var Message: TMessage); Publique Déclarations publiques fin;
Et c'est tout. Tous traités :)