Storlek på ComboBox rullgardinsbredd

TComboBox-komponenten kombinerar en redigeringsruta med en rullbar "pick" -lista. Användare kan välja ett objekt från listan eller skriva direkt i redigeringsrutan.

Listrutan

När en kombinationsruta är i tappat tillstånd ritar Windows en listbox typ av kontroll för att visa kombinationsruta för val.

De DropDownCount-egendom anger det maximala antalet objekt som visas i listrutan.

De bredden på listrutan skulle som standard motsvara kombinationsrutans bredd.

När artiklarnas längd (på en sträng) överstiger komboboxens bredd visas artiklarna som avstängd!

TComboBox ger inte ett sätt att ställa in bredden på sin rullgardinslista :(

Fixa ComboBox rullgardinsbredd

Vi kan ställa in bredden på listrutan genom att skicka ett speciellt Windows-meddelande till kombinationsrutan. Meddelandet är CB_SETDROPPEDWIDTH och skickar minsta tillåtna bredd, i pixlar, för listboxen i en kombinationsruta.

Om du vill koda storleken på rullgardinslistan till, låt oss säga, 200 pixlar, kan du göra:

 SendMessage (theComboBox.Handle, CB_SETDROPPEDWIDTH, 200, 0); 

Det här är bara ok om du är säker på att alla dina ComboBox.Items är inte längre än 200 px (när du drar).

För att säkerställa att vi alltid har rullgardinsmenyn tillräckligt bred kan vi beräkna önskad bredd.

Här är en funktion för att få önskad bredd på listrutan och ställa in den:

 procedur ComboBox_AutoWidth (const theComboBox: TCombobox); const HORIZONTAL_PADDING = 4; var itemsFullWidth: heltal; idx: heltal; artikelbredd: heltal; Börja itemsFullWidth: = 0; // få det maximala som behövs med föremålen i dropdown-tillstånd för idx: = 0 till -1 + theComboBox.Items.Count do Börja itemWidth: = theComboBox.Canvas.TextWidth (theComboBox.Items [idx]); Inc (artikelbredd, 2 * HORIZONTAL_PADDING); if (itemWidth> itemsFullWidth) sedan itemsFullWidth: = itemWidth; slutet; // ställa in bredden på rullgardinsmen om det behövs om (itemsFullWidth> theComboBox.Width) sedan Börja // kontrollera om det skulle finnas en rullningslista om theComboBox.DropDownCount < theComboBox.Items.Count sedan itemsFullWidth: = itemsFullWidth + GetSystemMetrics (SM_CXVSCROLL); SendMessage (theComboBox.Handle, CB_SETDROPPEDWIDTH, items FullWidth, 0); slutet; slutet; 

Bredden på den längsta strängen används för bredden på listrutan.

När man ska ringa ComboBox_AutoWidth?
Om du fyller i listan över objekt (vid designtid eller när du skapar formuläret) kan du ringa ComboBox_AutoWidth-proceduren i formulärets onCreate händelsehanterare.

Om du dynamiskt ändrar listan med objekt i kombinationsrutan kan du ringa proceduren ComboBox_AutoWidth inuti OnDropDown händelsehanterare - inträffar när användaren öppnar rullgardinslistan.

Ett test
För ett test har vi 3 kombinationsrutor på ett formulär. Alla har objekt med texten bredare än den faktiska kombinationsrutans bredd. Den tredje kombinationsrutan är placerad nära den högra kanten av formulärets gräns.

Egenskapen Objekt, för detta exempel, är förutfylld - vi kallar vår ComboBox_AutoWidth i händelsehanteraren OnCreate för formuläret:

 // Forms OnCreate procedur TForm.FormCreate (avsändare: TObject); Börja ComboBox_AutoWidth (ComboBox2); ComboBox_AutoWidth (ComboBox3); slutet; 

Vi har inte ringt ComboBox_AutoWidth för Combobox1 för att se skillnaden!

Observera att rullgardinsmenyn för Combobox2 när den körs kommer att vara bredare än Combobox2.

Hela rullgardinslistan är avskuren för "Nära höger kant placering"

För Combobox3, den som ligger nära höger kant, är rullgardinslistan avskuren.

Skicka CB_SETDROPPEDWIDTH kommer alltid att utöka listrutan till höger. När din kombobox är nära den högra kanten, skulle en utvidgning av listrutan till höger resultera i att listrutan skärs bort.

Vi måste på något sätt utöka listrutan till vänster när detta är fallet, inte till höger!

CB_SETDROPPEDWIDTH har inget sätt att ange i vilken riktning (vänster eller höger) att utöka listrutan.

Lösning: WM_CTLCOLORLISTBOX

Precis när rullgardinsmenyn ska visas skickar Windows WM_CTLCOLORLISTBOX-meddelandet till moderfönstret i en listruta - till vår kombinationsruta.

Att kunna hantera WM_CTLCOLORLISTBOX för den nästan högra kamoboxen skulle lösa problemet.

The Almighty WindowProc
Varje VCL-kontroll exponerar egenskapen WindowProc - proceduren som svarar på meddelanden som skickas till kontrollen. Vi kan använda egenskapen WindowProc för att tillfälligt ersätta eller underklassera fönstret för kontrollen.

Här är vår modifierade WindowProc för Combobox3 (den nära högra kanten):

 // modifierad ComboBox3 WindowProc procedur TForm.ComboBox3WindowProc (var Meddelande: TMessage); var cr, lbr: TRect; Börja // Rita listrutan med kombinerade objekt om Message.Msg = WM_CTLCOLORLISTBOX då Börja GetWindowRect (ComboBox3.Handle, cr); // listbox rektangel GetWindowRect (Message.LParam, lbr); // flytta den till vänster för att matcha höger kant om cr.Right lbr.Right sedan MoveWindow (Message.LParam, lbr.Left- (lbr.Right-clbr.Right), lbr.Top, lbr.Right-lbr.Left, lbr.Bottom-lbr.Top, True); slutet annan ComboBox3WindowProcORIGINAL (Message); slutet; 

Om meddelandet som vår kombinationsruta får är WM_CTLCOLORLISTBOX får vi dess fönster rektangel, vi får också rektangeln i listrutan som ska visas (GetWindowRect). Om det verkar som att listrutan skulle visas mer till höger - flyttar vi den till vänster så att kombinationsrutan och listrutan till höger är samma. Så lätt som det :)

Om meddelandet inte är WM_CTLCOLORLISTBOX kallar vi helt enkelt den ursprungliga proceduren för meddelandeshantering för kombinationsrutan (ComboBox3WindowProcORIGINAL).

Slutligen kan allt detta fungera om vi har ställt in det korrekt (i OnCreate-händelseshanteraren för formuläret):

 // Forms OnCreate procedur TForm.FormCreate (avsändare: TObject); Börja ComboBox_AutoWidth (ComboBox2); ComboBox_AutoWidth (ComboBox3); // bifoga modifierat / anpassat WindowProc för ComboBox3 ComboBox3WindowProcORIGINAL: = ComboBox3.WindowProc; ComboBox3.WindowProc: = ComboBox3WindowProc; slutet; 

Var i formulärdeklarationen har vi (hela):

 typ TForm = klass(TForm) ComboBox1: TComboBox; ComboBox2: TComboBox; ComboBox3: TComboBox; procedur FormCreate (avsändare: TObject); privat ComboBox3WindowProcORIGINAL: TWndMethod; procedur ComboBox3WindowProc (var Meddelande: TMessage); offentlig Offentliga förklaringar slutet; 

Och det är allt. Alla hanteras :)