C# Change ListView Item's/Row's height
Asked Answered
S

7

29

I want to change the Item's/Row's height in listview.

I searched every where and I figured that in order to change the height I need to use LBS_OWNERDRAWFIXED or MeasureItem or something like that.

The problem is that I dont know exactly what to do and how to use it..
Can anyone help me with it?

Edit:
I cant use the ImageList hack because I am using the SmallImageList for real and I need different line height from the ImageList images size.

Thanks!

Senna answered 3/7, 2011 at 15:51 Comment(3)
See #1244590. Simple solution.Leroylerwick
The "simple solution" mentioned is for WPF -- which is indeed simple. This question is regarding WinForms -- where it is decided not simple.Swathe
Use TreeView to simulate ListView behaviour. TreeView has had ItemHeight property.Plummer
S
15

It can be done using the SmallImageList trick -- you just have to be careful. ObjectListView -- an open source wrapper around a standard .NET ListView -- uses that trick to successfully implement a RowHeight property.

If you want 32 pixels for each row, allocate an ImageList that is 16x32 (width x height), and then position each of your images in the vertical middle of the 32-pixel height.

This screen shot shows 32-pixel rows and the word wrapping that is possible because of the extra space:

enter image description here

ObjectListView does all this work for you. In fact, if you are trying to do anything with a ListView, you should seriously looked at using an ObjectListView instead. It makes many difficult things (e.g. sorting by column type, custom tooltips) trivial, and several impossible things (e.g. overlays, groups on virtual lists) possible.

Swathe answered 4/7, 2011 at 13:51 Comment(2)
Well, I just wanted to resize row and 300kb for that is too much for the program I plan. anyway thats a good solution in general.Senna
I think you suggested a solution so complexNork
F
37

For the people that are still struggling with this, here is the code I use:

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

To use this, just do:

SetHeight(lvConnections, 25);
Ferous answered 6/2, 2015 at 10:47 Comment(1)
I tested this in detail mode. This trick works only to INCREASE the row height over the defaut height. You cannot make rows tighter than Windows defines. Please note the the height of a listview row depends on the operating system. For example on Windows 7 rows are significantly higher than on XP. Sadly this trick is not usefull for me.Handed
P
16

You have to use a bit of a hack. The trick is to use an image list in the StateImageList property. The ListView will adjust its item height, based on the height of the ImageList's ImageSize property. You do not have to specify an image for your items, but just using the StateImageList will force the ListView to adjust. In the example below, I had set the image list size to 32x32, thus resulting in a 32px height ListViewItem(s).

enter image description here

Paredes answered 3/7, 2011 at 17:48 Comment(3)
I forgot to say, I am using ImageList in SmallImageList proeprty so I cant use that hack.Senna
Then you're going to have to write your own control, because from experience and research, this is not going to be possible with the existing WinForms ListView. This was also the same response from a MSFT Community Support dev who also researched the issue for some folks, and it ended up in suggesting writing a custom control.Paredes
I tested this in detail mode. This trick works only to INCREASE the row height over the defaut height. You cannot make rows tighter than Windows defines. Please note the the height of a listview row depends on the operating system. For example on Windows 7 rows are significantly higher than on XP. Sadly this trick is not usefull for me.Handed
S
15

It can be done using the SmallImageList trick -- you just have to be careful. ObjectListView -- an open source wrapper around a standard .NET ListView -- uses that trick to successfully implement a RowHeight property.

If you want 32 pixels for each row, allocate an ImageList that is 16x32 (width x height), and then position each of your images in the vertical middle of the 32-pixel height.

This screen shot shows 32-pixel rows and the word wrapping that is possible because of the extra space:

enter image description here

ObjectListView does all this work for you. In fact, if you are trying to do anything with a ListView, you should seriously looked at using an ObjectListView instead. It makes many difficult things (e.g. sorting by column type, custom tooltips) trivial, and several impossible things (e.g. overlays, groups on virtual lists) possible.

Swathe answered 4/7, 2011 at 13:51 Comment(2)
Well, I just wanted to resize row and 300kb for that is too much for the program I plan. anyway thats a good solution in general.Senna
I think you suggested a solution so complexNork
H
10

Sadly nobody answered your original question how to use LBS_OWNERDRAWFIXED in all these years.

The answer that you have accepted is integrating a huge project (with demos and documentation 3,3MB). But just for setting the line height of a ListView this is overbloated.

The other workaround suggested here (adding an ImageList) works only to increase the row height. But it does not allow to really set the RowHeight independent of the image height. Additionally the default row height depends on the operating system. For example on Windows 7 the rows are much higher than on XP. You cannot chose to make them tighter, only higher.

But with very few lines you can do what you want. Just copy and paste the following class:

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

After adding a ListViewEx to your Form you will see a new property in the Visual Studio Forms Designer which allows to set the row height in pixels:

Setting RowHeight in a C# ListView

The value you enter there will be the row height in pixels and it will be respected exatctly on all operating systems. I tested it on Windows XP, 7 and 10:

ListViewEx.RowHeight sample

Additionally my class has two more advantages over the original ListView: It draws flicker-free and it respects the ForeColor and Font set in ListViewSubItem which is ignored by the original Microsoft ListView. So you can draw each cell with a different color and font.

IMPORTANT: As the MSDN says LBS_OWNERDRAWFIXED has been designed only for Details view (Report view). My code works only for this mode and this is because Microsoft has designed it like that.

Additionally please note that setting ListView.OwnerDraw = true is a completely different thing than using LVS_OWNERDRAWFIXED.

I did not implement drawing icons, because I don't need that. But you can easily add this.

Handed answered 6/11, 2017 at 16:4 Comment(8)
The CreateParams override is causing the subitem text to not display...Kalpa
I have absolutely no idea what you are talking about. I use this code in my application. It works perfectly! In the screenshots above you see that all subitems are drawn correctly, even in color.Handed
This code does not draw putting listviewitem.UseItemStyleForSubItemsproperty into consideration. If this property is set false then lisview needs to draw the backcolor of subitems. Please suggest how to achieve the same.Squarrose
It was the purpose of my code sample to answer the question, which my code does. If you need more functionality it is easy to change the code to your needs.Handed
Given this was answered at the end of 2017 it's too bad this doesn't work for UWP :(Ezzo
@kaylee: The entire namespace System.Windows.Forms will not work on UWP. So you are searching at the wrong place here.Handed
Sounds really great! And probably more CPU efficient than the generic scheme. Minor question: do we need to manually draw the grid lines in this mode? I would guess so.Stadium
@Alexis Martial: Just look at the 2 screenshots in my answer! The first one shows that Visual Studio already has an option named "GridLines" which I have set to True. The second screenshot shows that the grid lines are drawn. The reasson is that this is already done by Microsoft. My code draws the content of the cells, Microsoft draws the grid lines, the header and the border of the control.Handed
M
3

The default line height of a ListView (in report view mode) is computed based on the control's font size.

So to select the line height, choose a font with the right height in the ListView properties. For example, select MS Sans Serif 18.

Then you can change the font used by all items: when you insert a new item, set its font property.

To optimize font assignment you should declare the item font as a private member of the form:

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

Then when adding items :

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

This trick is the only easy one allowing to have SMALLER line height ;) i.E. set control's font size to 7 and set items' font size to 10. (Tested with VS 2008 )

Multilingual answered 13/10, 2014 at 8:55 Comment(2)
A downside to this is that the tooltip has the larger font, which can look a bit crazy.Bagnio
This does not answer the question. Making the font smaller does not change the height of the row.Handed
P
1

Plasmabubble has the right idea. This expands on that and is what I use to use a narrow line-width for the items.

The linespacing in a ListView is dependent on the ListView's font and can't be changed. However, you can set the font for the items in the ListView to something larger than the ListView's font.

If you want it to be proportional, create a font based on the item's font. I want the item height to be 90% of normal, whatever the font chosen.

When I populate the list I used a font stored in settings but you could also use a literal font like "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>
}
Pyridoxine answered 16/3, 2017 at 22:6 Comment(1)
This does not answer the question. Making the font smaller does not change the height of the row.Handed
V
0

After reading the answers for so many years, one is to use ListView based extension controls and the other is to use fonts or icons for extensions. If your project already uses a ListView -- as in my case, where you need to extend the column height while keeping the original icon and font size -- I suggest You can roughly calculate the column height you need, and the ratio of the normal icon size, and thus use the transparent border to extend the icon size, e.g. if the 24x24 icon is actually 35 in height, you can use windows paint 3D to extend the icon to 35x35 using the canvas to keep the original ratio of the icon, I think this is probably the most time and cost effective way.

Vergos answered 2/12, 2022 at 5:16 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Procathedral

© 2022 - 2024 — McMap. All rights reserved.