Article C0006 C# .NET
ListView essentials

This article is about some of the essential features of the ListView component. The following subjects will be discussed:
    How does indexing work within a ListView?
    How do SubItems work within a ListView?
    How can I add icons programmatically?
    How to automatically change the column width?
    How can I change the color of the lines in a details view?
    How can I uxTheme the ListView?

General
First of all, if you are going to fulfill some sort of action regarding an item in a ListView, first check if there is an item selected as follows:

    if (ListView.SelectedItems.Count > 0)
    {
    	//Do your work here
    }
Indexing in a ListView
Although the index works in all the available views within component, the effect is best explainable when the View property is set on Details. When an item is selected within a ListView its Selected property is set to True. Of course you’re able to run a foreach on the whole content looking for selected items, a better way to fulfill the job is to consult the SelectedIndices collection. When the MultiSelect property is set to False, you can simply call the following code to access the selected item:

    int selectedItem = ListView.SelectedIndices[0];
Since the MultiSelect is set to false the [0] value is the only one which could have a useful value. Now that we have saved the state of the index we can use it to restore the selected item after manipulation of the content of the list. When, for instance, the selected item is deleted or more items are added and the list is reloaded, it could be handy to put the focus on the last selected position in the list. You cannot simply restore the index by writing:

    ListView.SelectedIndices[0]=selectedItem;
nor
    ListView.SelectedIndices[0].Selected=selectedItem;
This due to the fact that the SelectedIndices property is read-only. The right solution to do this is to put the focus on the ListView, set the Selected value of the required item to true and programmatically select the item:

    ListView.Focus();
    ListView.Items[selectedItem].Selected=true;
    ListView.Select();
Or in total:

    // Save selected item:
    int selectedItem = ListView.SelectedIndices[0];
    
    // Update the ListView
    ..update content...
    
    // Restore selected item:
    ListView.Focus();
    ListView.Items[selectedItem].Selected = true;
    ListView.Select();
If the Select() method is not used the selection will not be visible as you should expected it to be. When you fulfill this task in a large list the item will be selected, it will receive the select focus, but it will still not be directly visible in the list since the ListView will not scroll to make the selected item visible. To programmatically make the selected item visible again call the EnsureVisible method of the ListView like this:

    ListView.EnsureVisible(selectedItem);
This will cause the ListView to scroll so that the item is visible.

SubItems in a ListView
SubItems will only be visible when the ListView is put into the detail view like this:

    ListView.View = View.Details;
Next you’ll have to add SubItems using the Columns ellipses within the ListView Properties windows or programmatically using the Columns.Add() method of the ListView. What to choose depends on the type of the program being created, but where applicable I prefer the lazy way, using the component properties window. To programmatically add an item to a ListView which contains two additional columns use the following code:

    ListViewItem item = new ListViewItem(contentA.ToString());
    item.SubItems.Add(contentB.ToString());
    item.SubItems.Add(contentC.ToString());
    ListView.Items.Add(item);
Once again the MultiSelect property is set to false and we have obtained the selected item using the following code:

    int selectedItem=ListView.SelectedIndices[0];
Now their information is accessible in the following way:

    LabelA.Text = ListView.SelectedItem[selectedItem].SubItems[0].Text;
    LabelB.Text = ListView.SelectedItem[selectedItem].SubItems[1].Text;
    LabelC.Text = ListView.SelectedItem[selectedItem].SubItems[2].Text;
As you can see the first columns is addressed as SubItems[0], but it can also be simply accessed like this:

    LabelA.Text = ListView.SelectedItem[selectedItem].Text;
But when using multiple columns in detailed view I always prefer the same access method for all columns.

To get a more in-depth understanding of the power of the ListView, take a look at the small program called CSVview available on this site.

Adding an icon programmatically
If you want to create a ListView containing a list with your photos, you can simply add a preview of the image dynamically when loading the list. Prior being able to work with images add an ImageList into your solution and assign is to the ListView. In one of my programs I just needed a 12x12 sized preview image, so I dragged and dropped an ImageList, set the ImageSize to 12;12 and the ColorDepth to Depth32Bit. For the sake of simplicity I load the list and the preview at a single pass and using a dynamically created PictureBox.

    PictureBox img = new PictureBox();
    img.Width =12;
    img.Height=12;
    img.SizeMode = PictureBoxSizeMode.StretchImage;
    int count=0; // Help integer for the image index
    foreach(…)
    {
        // Load the items here
        ListViewItem item = new ListViewItem(file.FileName); // Add the filename
        item.ImageIndex = count++; // Set the image number
        item.SubItems.Add(file.Size.ToString()); // Add a subitem
        img.Load(file); // Load the image in the picturebox
        ImageList.Images.Add(img.Image); // Put the image in the imagelist
        ListView.Add(item); // Add the item
    }
Do not forget to assign the ImageList to the SmallImageList property of the ListView. On large list you can add the Application.DoEvents() in the foreach loop to keep your program responsive to the user.

Automatically change the column width
You can automatically change the width of listview columns by using the ListView.AutoResizeColumn() or the ListView.AutoResizeColumns() methods. This value can be set in three different ways:

    ListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
    ListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
    ListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.None);
The ColumnHeaderAutoResizeStyle can be set to None, which means that nothing will happen with the column width. This is the default value of the resize style. The HeaderSize style will change the width of the column(s) into the size of the headers content (the Column.Text property). The third option is the one I usually use, the ColumnContent resize style. The width of the content of the ListView dictates the width of the column size.

Change the color of the lines
Changing the color of the lines gives your application a more professional look. It is wise not to use color to give the color of the lines some sort of weight. Like red lines are below zero and green lines above. This due to the fact that people who are for instance color blind, can misinterpret the meaning of those colors.

The following snippet show how to color each line. Remember that this is really effective then the View mode of the ListView is in Details. The used listview contains two columns and will contain 10 rows:

    int i = 0;
    var shaded = Color.FromArgb(190, 220, 250);
    for (int c=0; ^<10; c++)
                        {
                        var item=new listviewitem(c.tostring());
                        item.subitems.add("what a nice snippet!"); />/ Color the line:
        if (i++ % 2 == 1)
        {
            item.BackColor = shaded;
            item.UseItemStyleForSubItems = true;
        }
        lvProcess.Items.Add(item);
    }
The result of this snippet is shown here: Colorfull listview

How to uxTheme the ListView
The ListView can be tweaked to use the uxTheme by calling unmanaged code. So the first thing to be done is to add a reference towards the InteropServices library.

    using System.Runtime.InteropServices;
Next, the DllImport pointing to the unmanaged SetWindowTheme() method must be added within your class:

    [DllImport("uxtheme", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.I4)]
    public extern static Int32 SetWindowTheme(IntPtr hWnd,
        String textSubAppName,
        String textSubIdList);
Finally, the SetWindowTheme() method can be use (within the Form Load-event for instance) so that the ListView will use the theme:

    SetWindowTheme(lvUxed.Handle, "explorer", null);
The difference between a regular ListView and an uxTheme one is shown here:

uxTheme Usage
No uxTheme uxTheme
Colorfull listview

In both ListView's, row number one is selected and the mouse pointer is hovering above item number five.

The method is tested with Microsoft Vista and Microsoft Windows 7. I'm not sure if it's going to work with older client operatingsystems.

~Edward