Erik Porter's Blog

Life and Development at Microsoft and Other Technology Discussions

News

    WindowsForms ComboBox Auto DropDownList Width

    On a project I'm working on, I use some ComboBoxes that act as a breadcrumb control for a Wizard that I wrote, so the user can jump throughout the wizard to different steps easily and understand where they are.  Unfortunately, the ComboBoxes are dynamically filled from an admin section and can be anywhere from a few characters to a few hundred characters.  This would make the ComboBoxes very wide.  Unfortunately, the ComboBox doesn't automatically change the DropDownWidth Property based off of the items in the ComboBox.  I went ahead and wrote a Method to do that (This could be added into an inherited ComboBox to be used over and over too if you wanted) and thought I'd share:

    VB

    Private Sub FindWidthForDropDown(ByVal ComboBox As ComboBox)
        
    Dim g As Graphics = ComboBox.CreateGraphics()
        
    Dim IsDatabound As Boolean = Not ComboBox.DataSource Is Nothing AndAlso ComboBox.DisplayMember <> ""
        
    Dim WidestWidth As Integer = ComboBox.DropDownWidth
        
    Dim ValueToMeasure As String
        
    Dim CurrentWidth As Integer

         For i As Integer = 0 To ComboBox.Items.Count - 1
             
    If IsDatabound Then
                  
    ValueToMeasure = DirectCast(DirectCast(ComboBox.Items(i), DataRowView)(ComboBox.DisplayMember), String)
              Else
                  
    ValueToMeasure = DirectCast(ComboBox.Items(i), String)
             
    End If

              CurrentWidth = CType(g.MeasureString(ValueToMeasure, ComboBox.Font).Width, Integer)
             
    If CurrentWidth > WidestWidth Then WidestWidth = CurrentWidth
        
    Next

         ComboBox.DropDownWidth = WidestWidth

         g.Dispose()
    End Sub

    C#

    private void findWidthForDropDown(ComboBox comboBox)
    {
        
    bool isDatabound = comboBox.DataSource != null && comboBox.DisplayMember != null && comboBox.DisplayMember != "";
         int widestWidth = comboBox.DropDownWidth;
        
    string valueToMeasure;
        
    int currentWidth;

         using (Graphics g = comboBox.CreateGraphics())
         {
             
    for
    (int
    i = 0; i < comboBox.Items.Count; i++)
             
    {
                  
    if (isDatabound)
                       
    valueToMeasure = (
    string)((DataRowView)comboBox.Items[i])[comboBox.DisplayMember];
                  
    else
                       
    valueToMeasure = (string)ComboBox.Items[i];

                   currentWidth = (int)g.MeasureString(valueToMeasure, comboBox.Font).Width;
                  
    if (currentWidth > widestWidth) {widestWidth = currentWidth;}
              }
         }

         comboBox.DropDownWidth = widestWidth;
    }

    Simple, but hopefully helpful.  Just pass in the ComboBox and watch it's DropDownWidth change.

    UPDATE: Check out the comments for some additional things to add to this method suggested by commenter(s).

    Comments

    Wes said:

    I also wrote a method for doing this recently for a custom UserControl I wrote.

    Here is the code my code just for comparison purposes (please excuse the formatting):

    if (!this.comboBox1.IsHandleCreated || !(this.comboBox1.DataSource is IList))
    return;

    using (Graphics g = this.comboBox1.CreateGraphics())
    {
    int maxLength = 0;
    IList list = this.comboBox1.DataSource as IList;

    int numItems = Math.Min(list.Count, this.comboBox1.MaxDropDownItems);

    // Find the longest string in the first MaxDropDownItems
    for(int i = 0; i < numItems; i++)
    maxLength = Math.Max(maxLength,
    (int)g.MeasureString(this.comboBox1.GetItemText(list[i]), this.comboBox1.Font).Width);

    maxLength += 20; // Add a little buffer for the scroll bar

    // Make sure we are inbounds of the screen
    int left = this.PointToScreen(new Point(0, this.Left)).X;
    if (maxLength > Screen.PrimaryScreen.WorkingArea.Width - left)
    maxLength = Screen.PrimaryScreen.WorkingArea.Width - left;

    this.comboBox1.DropDownWidth = Math.Max(maxLength, this.comboBox1.Width);
    }

    If you notice one thing I different is use combobox.GetItemText(..) which I think is better than typecasting it to a DataRowView. One other suggestion is you may want to call dispose on your graphics object or just wrap it with a using statement.

    Wes
    # September 27, 2004 3:56 PM

    Erik Porter said:

    Ok, I'm getting rusty...thanks for pointing out that I forgot the Dispose (I'm embarassed now). As for the DataRowView, you're definitely right and using GetItemText would be better. I just copied it from an app I was working on and should also put that I make no promises that this is a perfect little piece of code. ;) I see you also have some other stuff like checking to make sure it doesn't go off the screen, etc. Good stuff for sure. I'll just add in the dispose to my code and let everyone see your code too...thanks! :)
    # September 27, 2004 4:28 PM

    Jayaram said:

    Nice Article. It solved my problem. Events in which this code should go are - DataSourceChanged,Layout.

    # February 28, 2007 4:35 PM

    HumanCompiler said:

    Jayaram, this was just a sample and was written two and a half years ago.  If you'd like it to "go" where you talk about, then you'll have to do it yourself.  :)

    # February 28, 2007 5:38 PM

    Mohon said:

    This is excellent. It is very helpful for me.

    # August 24, 2007 3:47 PM

    Ryan Hu said:

    but how to deal with the dropdown portion right edge out of the screen???

    # January 21, 2008 8:34 PM

    rosanera said:

    That saved me a lot of time. TY

    # February 13, 2008 7:59 AM

    Christophe said:

    Fantastic !  Thank you so much for sharing this code.

    # July 25, 2008 5:22 PM