This article is designed to introduce a developer to the Win32 Listview control object and how to create a small simple class for control over it. It is the hope of the author to help developers alleviate certain pains taken when creating a Listview object. While intended for the Win32 beginner, this article can not only be a nice suggestion for, but also a refresher for intermediate and advanced programmers. This article expects that the user is familiar with the Windows message pump and knows how to handle messages sent to the window.
In my beginner phase of programming (specifically Win32), I found it rather difficult to learn some of the coding procedures/Win32 control objects when they should be all too simple. It is my philosophy that the simpler, the better. Even if the design is totally complex, once you have a proper understanding, it should be all too simple. Yet, even with a proper understanding of the object, I still found myself spending hours on programming one object when it should have taken an hour, if not a half-hour. So with that, I decided to create a class object to handle a Listview control and will be presenting it here.
Using the code
Here I will present a Win32 Listview class. This class is designed to help a developer not only speed up the development time spent on a program but to also alleviate certain pains taken when dealing with Listview controls.
While my Listview class encompasses a little more than what is presented, below is the basis of my Listview class declaration. Additionally, I have defined this code in a separate .h and .cpp file for modability - so it can be used by more than one program. I suggest you do the same.
HWND create(int x, int y, int width, int height, char* styles, HWND pwnd);
HWND createfromresource(HWND pwnd, UINT id);
void setexstyles(char* styles);
bool addcolumn(int index, int width, char* title);
bool additem(int index, int subindex, char* value);
bool isselected(int index, int subindex);
The variables and routines declared should be easy enough to understand but I shall delve a bit more into them for clarification. If you're not familiar with it,
I'll point it out here: the
private call tells the program to restrict access of the following vars and routines to only the class's code access - meaning if you
try to reference it outside of class definitions, you'll get errors; the
public call tells the program to allow access everywhere, so long as the code
is referencing it correctly.
In the private declaration of the class we have a
HWND is a handle to the ListView control and provides us a way to communicate our desires to the ListView control.
In this way we can talk to it regardless if it is a dialog item or a window created with CreateWindowEx. We return the handle from create and createfromresource so if this class doesn't provide a certain kind of function, it can still be performed if you create a local or global
HWND and pass it to it.
LVCOLUMN is a typedef struct created for us by Windows and basically handles all the properties a column would need in a Listview control. Note that you don't have to have one
LVCOLUMN struct per column in the Listview control (meaning no arrays).
LVITEM is a typedef struct also created for us by Windows which handles all the properties of an item set up on the list. Note that you don't have to have one
LVITEM struct per item in the Listview control (meaning no arrays).
In the puplic declaration of the class the following routines (I'll provide a quick intro to them here too) are as follows:
HWND pwnd); -- This function will create the ListView control using CreateWindowEx. This enables us to create a Listview control in our program which won't require a DialogBox interface, should it be that is what is desired.
UINT id); -- I hope this one is obvious. This routine will return the handle to a DialogBox Listview control item so we can use it.
char* styles); -- This one might not be as obvious. If we can specify our styles when we use CreateWindowEx or during design time on the DialogBox resource in our environment, why do we need a setexstyles routine? Firstly, when we call our create routine, it does not set the extended styles. Secondly, during design time we cannot (for some unknown reason) set the extended styles on the Listview control. So, as a programmer who uses his brain, I created this function for simplisity's sake: regardless the handle (whether created from create or createfromresource) we can set the extended styles. This function is mainly directed at the DialogBox Listview control dysfunctionality but to save myself the headache of creating the extended styles in the create routine and for brevity of my code, this line serves perfectly for both kinds of Listveiw controls.
char* title); -- I hope this is another easy one. This will create a column for us with the specified title and width at the specified index (0-based).
char* value); -- This will create an item at the specified position with the specified text. Index is the row position, subindex the column position (again, 0-based).
void); -- What are we clearing here? The entire Listview control. This function will use the
LVM_DELETEALLITEMS message to do the job.
- bool isselected(int index, int subindex); -- This will ask the Listview control if the specified object is highlighted. This works excellently for multiselect Listview controls; simply put it in a
for loop and with the help of a
LVM_GETITEMCOUNT message and you can get the indexes of all selected items.
void); -- returns to us the number of selected (highlighted) items on the Listview control.
Now we get into the code. Here we will define your class routines, and then we'll move on to the window procedure we'll use to control the Listview object.
HWND listview::create(int x, int y, int width, int height, char* styles, HWND pwnd)
DWORD flags = WS_VISIBLE | WS_CHILD;
flags |= LVS_REPORT;
if(strstr(styles, "showsel") || strstr(styles, "show sel"))
flags |= LVS_SHOWSELALWAYS;
flags |= WS_BORDER;
flags |= WS_TABSTOP;
handle = CreateWindowEx(0, WC_LISTVIEW, "", flags, x, y,
width, height, pwnd, NULL, hinst, NULL);
HWND listview::createfromresource(HWND pwnd, UINT id)
handle = GetDlgItem(pwnd, id);
void listview::setexstyles(char* styles)
if(strstr(styles, "fullrow") || strstr(styles, "fullrowsel") ||
strstr(styles, "full row") || strstr(styles, "full sel"))
exflags |= LVS_EX_FULLROWSELECT;
exflags |= LVS_EX_HEADERINALLVIEWS;
exflags |= LVS_EX_COLUMNOVERFLOW;
SendMessage(handle, LVM_SETEXTENDEDLISTVIEWSTYLE, exflags, exflags);
bool listview::addcolumn(int index, int width, char* title)
bool success = FALSE;
lvc.mask = LVCF_WIDTH | LVCF_TEXT;
lvc.cx = width;
lvc.cchTextMax = strlen(title);
lvc.pszText = title;
if(SendMessage(handle, LVM_INSERTCOLUMN, index, (LPARAM)&lvc) != -1)
success = TRUE;
bool listview::additem(int index, int subindex, char* value)
lvi.mask = LVIF_TEXT;
lvi.iItem = index;
lvi.iSubItem = subindex;
lvi.cchTextMax = strlen(value);
lvi.pszText = value;
if(subindex != 0)
success = (bool)SendMessage(handle, LVM_SETITEM, 0, (LPARAM)&lvi);
if(SendMessage(handle, LVM_INSERTITEM, 0, (LPARAM)&lvi) != -1)
success = TRUE;
SendMessage(handle, LVM_DELETEALLITEMS, 0, 0);
bool listview::isselected(int index, int subindex)
lvi.mask = LVIF_STATE;
lvi.iItem = index;
lvi.iSubItem = subindex;
lvi.stateMask = LVIS_SELECTED;
SendMessage(handle, LVM_GETITEM, 0, (LPARAM)&lvi);
if(lvi.state == LVIS_SELECTED)
ishigh = TRUE;
return(SendMessage(handle, LVM_GETSELECTEDCOUNT, 0, 0));
Now the window proc. Under your main window procedure (we are assuming you are designing a Win32 program using
CreateWindowEx), you'll want to initialize the Listview control, set up the columns, and start adding items. Additionally, although I do not manipulate them, I will show you how to get to the Listview control notification messages for your window so you can manipulate them as you see fit.
int index=0, itemnum=0, colnum=0;
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
int rval=DefWindowProc(hwnd, msg, wp, lp);
list.create(5, 5, 0, 0, "tab border report showsel", parent);
list.setexstyles("fullrow header overflow");
list.addcolumn(0, 140, "Column One");
list.addcolumn(1, 140, "Column Two");
switch( (((NMHDR*)lp)->code) )
sprintf(af, "Item %d", index);
list.additem(index, 0, af);
list.additem(index++, 1, "Subitem 1");
colnum = ((LPNMLISTVIEW)lp)->iSubItem;
itemnum = ((LPNMLISTVIEW)lp)->iItem;
And that's it! Now you should have a very flexible piece of code with which to work. You can add routines and specifics to it (do try to ensure it remains as flexible
as possible) and you now can have full control over Listview Control items! Congratulations!
Something I think you might be interested in which I know I did not cover here: ImageLists. I know I did not cover them and I did it because I did not want
the beginner programmer to become overwhelmed by it. I might later add another article with improvements to the Listview class we've created here,
but I am sometimes of the opinion that when learning it online, everything's just copy-paste and the programmer learns nothing. In this way I present
a challenge to all beginner programmers to add the ImageList functionality to the class for their programs.