Failed to install DropDownHeight from ComboBox

I cannot correctly set DropDownHeight from ComboBox to display all elements.

I am using a control that inherits from ComboBox. I overridden the OnDrawItem and OnMeasureItem methods to create multiple columns and text wrapping in the column, if necessary. All of this works great.

The problem occurs when I try to install DropDownHeight. I set DropDownHeight to an arbitrarily large value, which was slightly larger than the list of items. The ComboBox control automatically disables any value for DropDownHeight that is larger than the size of all displayed items in the list. (Assuming you have a MaxDropDownItems property that is larger than the number of elements, which I do.) This behavior usually works fine, as shown below: alt text http://www.freeimagehosting.net/uploads/dd09404697.png

No, this is not my real data in the drop-down list.

The problem occurs when I have an entry in the drop-down list that I need to wrap in order to display the full text. This entry is displayed normally, but, however, the ComboBox calculates DropDownHeight, it ignores the fact that one of the entries is twice as high as usual, so you need to scroll one line to go to the last entry in the drop-down list. alt text http://www.freeimagehosting.net/uploads/d0ef715f83.png

This is the code I use to determine if an element is needed to wrap text and to set the height of each element:

 Protected Overrides Sub OnMeasureItem(ByVal e As System.Windows.Forms.MeasureItemEventArgs)
    MyBase.OnMeasureItem(e)
    //Determine the proper height of the current row in the dropdown based on
    //the length of the OptionDescription string.
    Dim tmpStr As String = FilterItemOnProperty(Items(e.Index), "OptionDescription")
    Dim lng As Single = e.Graphics.MeasureString(tmpStr, Me.Font).Width
    //Use the length of the item and the width of the column to calculate if wrapping is needed.
    Dim HeightMultiplier As Integer = Math.Floor(lng / _ColumnWidths(1)) + 1
    e.ItemHeight = e.ItemHeight * HeightMultiplier

 End Sub

I cannot determine how to make the DropDownHeight property be exactly the value I want, or how to tell the ComboBox manager that one (or more) of the items in the list is above normal.

Override Shadow the DropDownHeight, , , .

EDIT:
WPF, ? ( WPF, ?)

+5
4

, VB6 VB.NET. -, VB6, API SetWindowPos WM_CTLCOLORLISTBOX -, HWnd . , ComboBox , , , . , . , , newHeight, .

Private Structure RECT
    Public Left As Integer        'x position Of upper-left corner
    Public Top As Integer         'y position Of upper-left corner
    Public Right As Integer       'x position Of lower-right corner
    Public Bottom As Integer      'y position Of lower-right corner
End Structure

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, _
                         r.Left, _
                         r.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
+9

#.

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;        // x position of upper-left corner
        public int Top;         // y position of upper-left corner
        public int Right;       // x position of lower-right corner
        public int Bottom;      // y position of lower-right corner
    }

    public const int SWP_NOZORDER = 0x0004;
    public const int SWP_NOACTIVATE = 0x0010;
    public const int SWP_FRAMECHANGED = 0x0020;
    public const int SWP_NOOWNERZORDER = 0x0200;

    public const int WM_CTLCOLORLISTBOX = 0x0134;

    private int _hwndDropDown = 0;

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_CTLCOLORLISTBOX)
        {
            if (_hwndDropDown == 0)
            {
                _hwndDropDown = m.LParam.ToInt32();

                RECT r;
                GetWindowRect((IntPtr)_hwndDropDown, out r);

                //height of four items plus 2 pixels for the border in my test
                int newHeight;

                if (Items.Count <= MaxDropDownItems)
                {
                    newHeight = Items.Count * ItemHeight + 2;
                }
                else
                {
                    newHeight = MaxDropDownItems * ItemHeight + 2;
                }

                SetWindowPos((IntPtr)_hwndDropDown, IntPtr.Zero,
                    r.Left,
                             r.Top,
                             DropDownWidth,
                             newHeight,
                             SWP_FRAMECHANGED |
                                 SWP_NOACTIVATE |
                                 SWP_NOZORDER |
                                 SWP_NOOWNERZORDER);
            }
        }

        base.WndProc(ref m);
    }

    protected override void OnDropDownClosed(EventArgs e)
    {
        _hwndDropDown = 0;
        base.OnDropDownClosed(e);
    }
+2

MyBase.OnMeasureItem

0

Edit: , :

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();
    }
}

, DrawMode OwnerDrawVariable, . , DrawItem. MSDN.

0

All Articles