Hello, this is my first article here, so please, if you can, bare with me. Also, my English can be "different", and I apologize for any mistakes.
OK, first of all, the problem: I needed to map an object's properties to a set of
ListView columns and store the object itself within a
ListViewItem. Why? Because one of the greatest things of OOP is that you can create a class that would contain a set of data and methods that operate with that data, and use it in a very elegant way. Because I like working with objects instead of any other method of storing data, I wanted to make the
ListViewItem hold an object and automatically map its properties to a set of columns from a
The solution: The
ListViewExtendedItem class. This class inherits
ListViewItems, and adds a new property, a different constructor, and a (two if you count the overload) method.
How it Works?
Well, because I make my own classes that hold the data I'm interested in keeping, I have full control over how I build them up. Also, I have complete control over what columns to display and their order. Because of this, I can use custom attributes to mark the properties of the data classes with the corresponding column name. A simple solution, but with a big disadvantage: no other class may be used except those that have been written with this custom attribute.
- The custom attribute,
This is the attribute we use to mark the properties with the column name where they should be mapped to. It is a very basic attribute, as you can see below. It only has one constructor that takes a string as a parameter (the column name) and overrides the
ToString() method so that when we later check for the column name, it is a bit easier.
public class ListViewColumnAttribute : Attribute
private string _columnName;
public ListViewColumnAttribute(string columnName)
_columnName = columnName;
public override string ToString()
So far so good, nothing too fancy going on. We will use this attribute when we write our classes like this:
This is the class that does all the work. It inherits the
ListViewItem class and adds the following:
- A very different constructor,
ListViewExtendedItem(ListView parentListView, object data), where
parentListView is the
ListView control that will hold the item. Its columns have to be setup before you create any extended item, and
data is the object you want to add to display.
- A property,
Data of type
object that holds the object itself; useful if your objects are able to perform several tasks (for instance, my objects are able to print themselves; all I have to do is add an extended item to the ListView, and a button that calls the
Print() method of the object, for each selected item in the
Update method with two overloads,
Update(object data) and
Update(object data, ListView listView), that does the actual mapping from the object's properties to the columns. The first overload takes only one argument, a new (updated) object that has to be displayed. It relies on the
ListView property from the base
ListViewItem class to get its column information. If it is no set (the item has not yet been added to a
ListView) it will throw an exception. The second overload asks for an explicit
OK, the actual code that does all the work is contained within the body of the second overload of the
Update method. Both the constructor and the first overload calls this one to do the job. Here is the code:
public void Update(object data, ListView listView)
Type typeOfData = data.GetType();
bool completed_column = false;
foreach (ColumnHeader column in listView.Columns)
completed_column = false;
foreach (PropertyInfo pInfo in typeOfData.GetProperties())
foreach (object pAttrib in pInfo.GetCustomAttributes(true))
if (pAttrib.GetType() == typeof(ListViewColumnAttribute))
if (pAttrib.ToString() == column.Name)
if (column.DisplayIndex == 0)
this.Text = pInfo.GetValue(data, null).ToString();
completed_column = true;
completed_column = true;
_data = data;
First, we clear all the
SubItems, so there won't be any other columns besides the ones needed. Then, we get the
Type of the data object, and we define a
bool variable that will help optimize the method a bit. Then, for each column the provided
ListView control has, we have to check the object's properties if one of them has been marked with a custom attribute of type
ListViewColumnAttribute and if the attribute has the same column name as the column we are currently searching for. The code is not really pleasant, three nested
foreach and some
ifs along the way are not pleasant to the eyes. However, it does the job done, and for the time being, I cannot think of another way to check the members of a type. After one member is found that has been marked with a matching attribute and the column name matches the one stored by the attribute, we proceed to check if the column we are populating is the first one (
DisplayIndex=0) because the item's own text property is displayed, instead of the text held by a
Notice that there is an
if statement checking the value of
completed_column. This is to ensure that after a match has been found, the code does not linger and search any more, but goes to the next column. After all the columns are done, the internal
_data object is assigned the one passed to the function.
This is pretty much all there is to it. The
ListViewExtendedItem can be added to the
Items collection of the
ListView control, and it will display the marked properties on the right column.
The first obvious annoying thing is that when you retrieve an extended item, you have to type cast it to
ListViewExtenededItem in order to make use of it, and also, you have to unbox the
Data property to access its members. This can all be resolved with a generic implementation of the class; however, for the time being, several things about generics escape me.
Note that I have not tested if this works while using Virtual Items in the
ListView, but it should work, I don't see what would break it... Also, if the data object properties have some obscure type that does not override the
ToString() method, the results are not going to be very helpful. This can be solved by using attributes again to store the name of a certain field you want to show from that obscure object (maybe add an interface that would provide a method to retrieve a property name so that it can be changed at runtime).
And, the last annoying thing, Visual Studio won't update the
Name property of the
ColumnHeaders used in the
ListView, you have to set them up in code.
Source Code and Demo App
In the download (link above), you will find a demo app that makes use of this class. The usage is pretty straightforward. Use the New button to create a new object instance, modify its properties with the property grid, then click Add to save it in the list view. If you click on an item in the list view, you will see the data object's properties in the property grid. After you modify them, click on Update to save the changes and display it in the list view control.
ListViewColumnAttribute can be found in the file with the same name, and the same goes for the
Thanks for reading this article. I hope it helps. Let me know if you like/don't like something about the extended item class.