A: 

Try to call MyBase.OnMeasureItem at the end of the method

Thomas Levesque
I've tried calling MyBase.OnMeasureItem before my code, after it, and even leaving it out entirely. All with no effect. Thanks for responding though. I was beginning to think I would wind up with a tumbleweed on this one.
Stewbob
+3  A: 

I'm trying to solve this exact same problem myself right at the moment for an application that I am migrating from VB6 to VB.NET. The owner-drawn combo control I have in VB6 sets the height of the drop-down through a SetWindowPos API call in response to the WM_CTLCOLORLISTBOX message on the combo control, which gives us access to the HWnd for the drop-down list of the combo control. The following code was added to my class that inherits from ComboBox and seems to do the trick, but still needs testing. I'm not sure it's the most elegant way of doing this either. Obviously you'll need to change the line that sets the newHeight variable, but this should give you the general idea.

Private Declare Function GetWindowRect Lib "user32" _
    (ByVal hwnd As Integer, ByRef lpRect As RECT) As Integer

Private Declare Sub SetWindowPos Lib "user32" _
    (ByVal hwnd As Integer, ByVal hWndInsertAfter As Integer, _
     ByVal X As Integer, ByVal Y As Integer, _
     ByVal cx As Integer, ByVal cy As Integer, _
     ByVal wFlags As Integer)

Private Const SWP_NOZORDER As Integer = &H4
Private Const SWP_NOACTIVATE As Integer = &H10
Private Const SWP_FRAMECHANGED As Integer = &H20
Private Const SWP_NOOWNERZORDER As Integer = &H200

Private _hwndDropDown As Integer = 0

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    Const WM_CTLCOLORLISTBOX As Integer = &H134

    If m.Msg = WM_CTLCOLORLISTBOX Then
        If _hwndDropDown = 0 Then
            _hwndDropDown = m.LParam.ToInt32

            Dim r As RECT
            GetWindowRect(m.LParam.ToInt32, r)

            // height of four items plus 2 pixels for the border in my test
            Dim newHeight As Integer = 4 * MyBase.ItemHeight + 2

            SetWindowPos(m.LParam.ToInt32, 0, _
                         tr.Left, _
                         tr.Top, _
                         MyBase.DropDownWidth, _
                         newHeight, _
                         SWP_FRAMECHANGED Or _
                             SWP_NOACTIVATE Or _
                             SWP_NOZORDER Or _
                             SWP_NOOWNERZORDER)
        End If
    End If

    MyBase.WndProc(m)
End Sub

Protected Overrides Sub OnDropDownClosed(ByVal e As System.EventArgs)
    _hwndDropDown = 0
    MyBase.OnDropDownClosed(e)
End Sub
JDHnz
JDHnz, thanks for your response. I'm trying to avoid hijacking the Windows Messages, but your solution looks like it could work for me if I can't find another way. I would need to add some extra functionality to my control that stores the ItemHeight for each item in the combobox, but that shouldn't be too hard.
Stewbob
Thanks JDHnz. I was able to successfully implement this into my application.
Stewbob
A: 

Edit: I just tried to reproduce your problem but everything works fine:

class MyCustomComboBox : ComboBox
{
    public MyCustomComboBox()
    {
        DrawMode = DrawMode.OwnerDrawVariable;

        DropDownHeight = 255;
        DropDownWidth = 300;
        MaxDropDownItems = 20;
    }

    protected override void OnMeasureItem(MeasureItemEventArgs e)
    {
        base.OnMeasureItem(e);

        if (e.Index % 2 == 0)
            e.ItemHeight = ItemHeight * 3;
        else
            e.ItemHeight = ItemHeight * 2;
    }

    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        base.OnDrawItem(e);

        // Draw the background of the item.
        e.DrawBackground();

        Rectangle rectangle = new Rectangle(2, e.Bounds.Top + 2,
                e.Bounds.Height, e.Bounds.Height - 4);
        e.Graphics.FillRectangle(new SolidBrush(Color.Gray), rectangle);

        Font myFont = new Font(FontFamily.GenericSansSerif, 30, FontStyle.Bold);
        e.Graphics.DrawString(this.Items[e.Index] as string, myFont, Brushes.Black,
            new RectangleF(e.Bounds.X + rectangle.Width, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height));

        // Draw the focus rectangle if the mouse hovers over an item.
        e.DrawFocusRectangle();
    }
}

If I remember correctly you have to set the property DrawMode to OwnerDrawVariable to enable drawing custom item heights. If you do this you'll also have to handle the DrawItem event. Have a look at the property help in MSDN.

Patrick M.
I set the DrawMode to OwnerDrawVariable in the constructor, and Override the DrawItem event. That's how I get the multi-column display, the alternating background colors, and the text-wrapping in the drop-down.
Stewbob