C # Изменить высоту элемента / строки ListView

Я хочу изменить высоту элемента / строки в списке.

Я обыскал повсюду и решил, что для изменения высоты мне нужно использовать LBS_OWNERDRAWFIXED или MeasureItem или что-то в этом роде.

Проблема в том, что я точно не знаю, что делать и как этим пользоваться.
Может ли кто-нибудь мне с этим помочь?

Изменить:
Я не могу использовать взлом ImageList, потому что я использую SmallImageList по-настоящему, и мне нужна высота строки, отличная от размера изображений ImageList.

Спасибо!


person Ron    schedule 03.07.2011    source источник
comment
См. stackoverflow.com/questions/ 1244090 /. Простое решение.   -  person    schedule 03.06.2014
comment
Упомянутое простое решение предназначено для WPF, что действительно просто. Этот вопрос по WinForms - там решено непросто.   -  person Grammarian    schedule 18.05.2015
comment
Используйте TreeView для имитации поведения ListView. TreeView имеет свойство ItemHeight.   -  person 23W    schedule 25.04.2019


Ответы (6)


Это можно сделать с помощью трюка SmallImageList - просто нужно быть осторожным. ObjectListView - оболочка с открытым исходным кодом для стандартной .NET ListView - использует этот трюк для успешной реализации свойства RowHeight.

Если вам нужно 32 пикселя для каждой строки, выделите ImageList размером 16x32 (ширина x высота), а затем разместите каждое из ваших изображений посередине по вертикали на высоте 32 пикселя.

На этом снимке экрана показаны 32-пиксельные строки и перенос слов, который возможен из-за лишнего места:

введите описание изображения здесь

ObjectListView делает всю эту работу за вас. Фактически, если вы пытаетесь сделать что-либо с помощью ListView, вам следует серьезно подумать об использовании вместо этого ObjectListView. Это делает многие сложные вещи (например, сортировку по типу столбца, настраиваемые всплывающие подсказки) тривиальными и несколько невозможных вещей (например, наложения, группы в виртуальных списках) возможными.

person Grammarian    schedule 04.07.2011
comment
Ну, я просто хотел изменить размер строки, и 300кб для этого слишком много для программы, которую я планирую. в любом случае это хорошее решение в целом. - person Ron; 04.07.2011
comment
Я думаю, вы предложили такое сложное решение - person John Nguyen; 21.08.2014

Для людей, которые все еще борются с этим, вот код, который я использую:

private void SetHeight(ListView listView, int height)
{
    ImageList imgList = new ImageList();
    imgList.ImageSize = new Size(1, height);
    listView.SmallImageList = imgList;
}

Чтобы использовать это, просто выполните:

SetHeight(lvConnections, 25);
person Maarten Peels    schedule 06.02.2015
comment
Я тестировал это в подробном режиме. Этот трюк работает только для УВЕЛИЧЕНИЯ высоты строки по сравнению с высотой по умолчанию. Вы не можете сделать строки более плотными, чем определяет Windows. Обратите внимание, что высота строки списка зависит от операционной системы. Например, в Windows 7 строки значительно выше, чем в XP. К сожалению, этот трюк мне не пригодился. - person Elmue; 04.11.2017

Вы должны использовать немного хитрости. Уловка состоит в том, чтобы использовать список изображений в свойстве StateImageList. ListView будет регулировать высоту своего элемента в зависимости от высоты свойства ImageSize ImageList. Вам не нужно указывать изображение для ваших элементов, но простое использование StateImageList заставит ListView настроиться. В приведенном ниже примере я установил размер списка изображений 32x32, в результате чего ListViewItem (s) высотой 32 пикселя.

введите описание изображения здесь

person David Anderson    schedule 03.07.2011
comment
Я забыл сказать, что я использую ImageList в проекте SmallImageList, поэтому я не могу использовать этот хак. - person Ron; 03.07.2011
comment
Затем вам придется написать свой собственный элемент управления, потому что, исходя из опыта и исследований, это невозможно с существующим WinForms ListView. Это был тот же ответ от разработчика MSFT Community Support, который также исследовал проблему для некоторых людей, и в итоге он предложил написать настраиваемый элемент управления. - person David Anderson; 03.07.2011
comment
Я тестировал это в подробном режиме. Этот трюк работает только для УВЕЛИЧЕНИЯ высоты строки по сравнению с высотой по умолчанию. Вы не можете сделать строки более плотными, чем определяет Windows. Обратите внимание, что высота строки списка зависит от операционной системы. Например, в Windows 7 строки значительно выше, чем в XP. К сожалению, этот трюк мне не пригодился. - person Elmue; 04.11.2017

К сожалению, никто не ответил на ваш первоначальный вопрос, как использовать LBS_OWNERDRAWFIXED за все эти годы.

Ответ, который вы приняли, - интеграция огромного проекта (с демонстрациями и документацией 3,3 МБ). Но просто для установки высоты строки ListView это завышено.

Другой предлагаемый здесь обходной путь (добавление ImageList) работает только для увеличения высоты строки. Но это не позволяет действительно установить RowHeight независимо от высоты изображения. Кроме того, высота строки по умолчанию зависит от операционной системы. Например, в Windows 7 строки намного выше, чем в XP. Вы не можете сделать их жестче, а только выше.

Но с очень небольшим количеством строк вы можете делать то, что хотите. Просто скопируйте и вставьте следующий класс:

using System;
using System.Drawing;
using System.Diagnostics;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ExtendedControls
{

public class ListViewEx : ListView
{
    #region Windows API

    /*
    struct MEASUREITEMSTRUCT 
    {
        public int    CtlType;     // Offset = 0
        public int    CtlID;       // Offset = 1
        public int    itemID;      // Offset = 2
        public int    itemWidth;   // Offset = 3
        public int    itemHeight;  // Offset = 4
        public IntPtr itemData;
    }
    */

    [StructLayout(LayoutKind.Sequential)]
    struct DRAWITEMSTRUCT
    {
        public int    ctlType;
        public int    ctlID;
        public int    itemID;
        public int    itemAction;
        public int    itemState;
        public IntPtr hWndItem;
        public IntPtr hDC;
        public int    rcLeft;
        public int    rcTop;
        public int    rcRight;
        public int    rcBottom;
        public IntPtr itemData;
    }

    // LVS_OWNERDRAWFIXED: The owner window can paint ListView items in report view. 
    // The ListView control sends a WM_DRAWITEM message to paint each item. It does not send separate messages for each subitem. 
    const int LVS_OWNERDRAWFIXED = 0x0400;
    const int WM_SHOWWINDOW      = 0x0018;
    const int WM_DRAWITEM        = 0x002B;
    const int WM_MEASUREITEM     = 0x002C;
    const int WM_REFLECT         = 0x2000;

    #endregion

    bool mb_Measured = false;
    int  ms32_RowHeight = 14;

    /// <summary>
    /// Constructor
    /// </summary>
    public ListViewEx()
    {
        SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    }

    /// <summary>
    /// Sets the row height in Details view
    /// This property appears in the Visual Studio Form Designer
    /// </summary>
    [Category("Appearance")]  
    [Description("Sets the height of the ListView rows in Details view in pixels.")] 
    public int RowHeight
    {
        get { return ms32_RowHeight; }
        set 
        { 
            if (!DesignMode) Debug.Assert(mb_Measured == false, "RowHeight must be set before ListViewEx is created.");
            ms32_RowHeight = value; 
        }
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams k_Params = base.CreateParams;
            k_Params.Style |= LVS_OWNERDRAWFIXED;
            return k_Params;
        }
    }

    /// <summary>
    /// The messages WM_MEASUREITEM and WM_DRAWITEM are sent to the parent control rather than to the ListView itself.
    /// They come here as WM_REFLECT + WM_MEASUREITEM and WM_REFLECT + WM_DRAWITEM
    /// They are sent from Control.WmOwnerDraw() --> Control.ReflectMessageInternal()
    /// </summary>
    protected override void WndProc(ref Message k_Msg)
    {
        base.WndProc(ref k_Msg); // FIRST

        switch (k_Msg.Msg)
        {
            case WM_SHOWWINDOW: // called when the ListView becomes visible
            {
                Debug.Assert(View == View.Details, "ListViewEx supports only Details view");
                Debug.Assert(OwnerDraw == false,   "In ListViewEx do not set OwnerDraw = true");
                break;
            }
            case WM_REFLECT + WM_MEASUREITEM: // called once when the ListView is created, but only in Details view
            {
                mb_Measured = true;

                // Overwrite itemHeight, which is the fifth integer in MEASUREITEMSTRUCT 
                Marshal.WriteInt32(k_Msg.LParam + 4 * sizeof(int), ms32_RowHeight);
                k_Msg.Result = (IntPtr)1;
                break;
            }
            case WM_REFLECT + WM_DRAWITEM: // called for each ListViewItem to be drawn
            {
                DRAWITEMSTRUCT k_Draw = (DRAWITEMSTRUCT) k_Msg.GetLParam(typeof(DRAWITEMSTRUCT));
                using (Graphics i_Graph = Graphics.FromHdc(k_Draw.hDC))
                {
                    ListViewItem i_Item = Items[k_Draw.itemID];

                    Color c_BackColor = i_Item.BackColor;
                    if (i_Item.Selected) c_BackColor = SystemColors.Highlight;
                    if (!Enabled)        c_BackColor = SystemColors.Control;

                    using (SolidBrush i_BackBrush = new SolidBrush(c_BackColor))
                    {
                        // Erase the background of the entire row
                        i_Graph.FillRectangle(i_BackBrush, i_Item.Bounds);
                    }

                    for (int S=0; S<i_Item.SubItems.Count; S++)
                    {
                        ListViewItem.ListViewSubItem i_SubItem = i_Item.SubItems[S];

                        // i_Item.SubItems[0].Bounds contains the entire row, rather than the first column only.
                        Rectangle k_Bounds = (S>0) ? i_SubItem.Bounds : i_Item.GetBounds(ItemBoundsPortion.Label);

                        // You can use i_Item.ForeColor instead of i_SubItem.ForeColor to get the same behaviour as without OwnerDraw
                        Color c_ForeColor = i_SubItem.ForeColor;
                        if (i_Item.Selected) c_ForeColor = SystemColors.HighlightText;
                        if (!Enabled)        c_ForeColor = SystemColors.ControlText;

                        TextFormatFlags e_Flags = TextFormatFlags.NoPrefix | TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine;
                        switch (Columns[S].TextAlign)
                        {
                            case HorizontalAlignment.Center: e_Flags |= TextFormatFlags.HorizontalCenter; break;
                            case HorizontalAlignment.Right:  e_Flags |= TextFormatFlags.Right; break;
                        }

                        TextRenderer.DrawText(i_Graph, i_SubItem.Text, i_SubItem.Font, k_Bounds, c_ForeColor, e_Flags);
                    }
                }
                break;
            }
        }
    }
} // class
} // namespace

После добавления ListViewEx в вашу форму вы увидите новое свойство в конструкторе форм Visual Studio, которое позволяет установить высоту строки в пикселях:

«Установка

Введенное вами значение будет высотой строки в пикселях, и оно будет точно соблюдаться во всех операционных системах. Я тестировал его на Windows XP, 7 и 10:

ListViewEx.RowHeight sample

Кроме того, у моего класса есть еще два преимущества по сравнению с исходным ListView: он рисует без мерцания и соблюдает ForeColor и Font, установленные в ListViewSubItem, который игнорируется исходным Microsoft ListView. Таким образом, вы можете нарисовать каждую ячейку другим цветом и шрифтом.

ВАЖНО: Как указано в MSDN, LBS_OWNERDRAWFIXED был разработан только для представления подробностей (представления отчета). Мой код работает только в этом режиме, и это потому, что Microsoft разработала его именно таким.

Кроме того, обратите внимание, что установка ListView.OwnerDraw = true - это совсем другое дело, чем использование LVS_OWNERDRAWFIXED.

Рисование иконок я не реализовал, потому что мне это не нужно. Но вы легко можете это добавить.

person Elmue    schedule 06.11.2017
comment
Переопределение CreateParams приводит к тому, что текст подэлемента не отображается ... - person user1932634; 06.03.2018
comment
Я совершенно не понимаю, о чем вы говорите. Я использую этот код в своем приложении. Работает отлично! На скриншотах выше вы видите, что все подпункты нарисованы правильно, даже в цвете. - person Elmue; 08.03.2018
comment
Этот код не учитывает listviewitem.UseItemStyleForSubItemsproperty. Если для этого свойства установлено значение false, то lisview должен отрисовывать задний цвет подэлементов. Подскажите, пожалуйста, как добиться того же. - person JDoshi; 04.12.2018
comment
Целью моего примера кода было ответить на вопрос, который и делает мой код. Если вам нужна дополнительная функциональность, можно легко изменить код в соответствии с вашими потребностями. - person Elmue; 05.12.2018
comment
Учитывая, что на этот вопрос ответили в конце 2017 года, жаль, что это не работает для UWP :( - person kayleeFrye_onDeck; 06.06.2019
comment
@kaylee: все пространство имен System.Windows.Forms не будет работать в UWP. Значит, вы ищете здесь не в том месте. - person Elmue; 10.06.2019

Высота строки по умолчанию для ListView (в режиме просмотра отчета) вычисляется на основе размера шрифта элемента управления.

Итак, чтобы выбрать высоту строки, выберите шрифт с правильной высотой в свойствах ListView. Например, выберите MS Sans Serif 18.

Затем вы можете изменить шрифт, используемый всеми элементами: когда вы вставляете новый элемент, установите его свойство font.

Чтобы оптимизировать назначение шрифтов, вы должны объявить шрифт элемента как закрытый член формы:

Private Font stdfont = new Font( "Consolas", 9.0f, FontStyle.Regular );

Затем при добавлении предметов:

ListViewItem i = new ListViewItem( "some text" );
i.Font = stdfont;
MyListView.Items.Add( i );

Этот трюк - единственный простой, позволяющий иметь МЕНЬШУЮ высоту строки;) т.е. установите размер шрифта элемента управления равным 7 и установите размер шрифта элементов равным 10. (Протестировано с VS 2008)

person Plasmabubble    schedule 13.10.2014
comment
Обратной стороной этого является то, что всплывающая подсказка имеет более крупный шрифт, что может выглядеть немного сумасшедшим. - person Drew Noakes; 13.06.2018
comment
Это не отвечает на вопрос. Уменьшение шрифта не влияет на высоту строки. - person Elmue; 19.05.2020

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

Межстрочный интервал в ListView зависит от шрифта ListView и не может быть изменен. Однако вы можете установить для элементов в ListView шрифт большего размера, чем шрифт ListView.

Если вы хотите, чтобы он был пропорциональным, создайте шрифт на основе шрифта элемента. Я хочу, чтобы высота элемента составляла 90% от нормы, независимо от выбранного шрифта.

Когда я заполнял список, я использовал шрифт, хранящийся в настройках, но вы также могли использовать буквальный шрифт, такой как «Consolas».

lvResults.Font = 
   new Font(Properties.Settings.Default.usrHookFont.FontFamily, 
      (float)(Properties.Settings.Default.usrHookFont.Size * .9));

foreach (HookSet item in resultSet)
   {
      ListViewItem lvi = new ListViewItem();
      lvi.Font = Properties.Settings.Default.usrHookFont;
      <dot><dot><dot>
}
person Dana Bell    schedule 16.03.2017
comment
Это не отвечает на вопрос. Уменьшение шрифта не влияет на высоту строки. - person Elmue; 19.05.2020