Невозможно установить DropDownHeight ComboBox - PullRequest
5 голосов
/ 07 августа 2009

Я не могу правильно установить DropDownHeight ComboBox для отображения всех элементов.

Я использую элемент управления, который наследуется от ComboBox. Я переопределил методы OnDrawItem и OnMeasureItem, чтобы создать несколько столбцов и перенос текста в столбце, если это необходимо. Это все отлично работает.

Проблема возникает, когда я пытаюсь установить DropDownHeight. Я установил DropDownHeight на произвольно большое значение, чуть больше, чем список элементов. Элемент управления ComboBox автоматически обрезает любое значение для DropDownHeight, превышающее размер всех отображаемых элементов в списке. (Предполагая, что у вас свойство MaxDropDownItems установлено выше, чем количество элементов, что я делаю.) Обычно это поведение работает отлично, как показано ниже: альтернативный текст http://www.freeimagehosting.net/uploads/dd09404697.png

Нет, это не мои реальные данные в раскрывающемся списке.

Проблема возникает, когда в раскрывающемся списке есть запись, которую необходимо перенести, чтобы отобразить полный текст. Эта запись отображается нормально, но, хотя ComboBox вычисляет DropDownHeight, он игнорирует тот факт, что одна из записей в два раза больше обычного, поэтому вам нужно прокрутить одну строку вниз, чтобы перейти к последней записи в раскрывающемся списке. альтернативный текст http://www.freeimagehosting.net/uploads/d0ef715f83.png

Это код, который я использую, чтобы определить, нуждается ли элемент в переносе текста и установить высоту каждого элемента:

 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

Я не могу определить, как заставить свойство DropDownHeight быть именно тем значением, которое я хочу, или как сообщить элементу управления ComboBox, что один (или более) элементов в списке выше, чем обычно.

Я пытался переопределить Затенить свойство DropDownHeight, но это, похоже, не оказало влияния.

EDIT:
Может ли переход на WPF решить эту проблему? (Достаточно ли настраиваемости в стандартных элементах управления WPF, чтобы мне не нужно было писать пользовательский элемент управления для комбинированного списка с 3 столбцами переменной высоты?)

Ответы [ 4 ]

9 голосов
/ 26 августа 2009

Я сейчас пытаюсь решить эту проблему точно так же для приложения, которое я мигрирую с 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
2 голосов
/ 26 марта 2011

Вот версия принятого ответа на c #.

    [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);
    }
0 голосов
/ 26 августа 2009

Изменить: Я просто попытался воспроизвести вашу проблему, но все работает нормально:

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 голосов
/ 10 августа 2009

Попробуйте позвонить MyBase.OnMeasureItem в конце метода

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...