Creating a List View Control

At first glance, creating a list view control might appear to be a daunting task. Getting all the necessary information placed in the correct structures involves several steps:

  1. Create the window by using the CreateWindow or CreateWindowEx function, specifying WC_LISTVIEW as the class name. Alternatively, if you are writing in MFC, use the MFC CListCtrl class and its Create member function.
  2. Create image lists for the large icon and small icon views by calling ImageList_Create. Load the bitmaps for the images by calling LoadBitmap or LoadIcon, and add them to the image lists by calling ImageList_Add or ImageList_AddIcon. Alternatively, use the MFC CImageList class.
  3. Initialize the column header you will use by loading the strings and calling ListView_InsertColumn. MFC developers should use the CListCtrl class.
  4. Insert each item into the list view control, and initialize any associated text.

The following code demonstrates these steps. In the LISTVIEW and MFCLIST samples, I defined a structure containing information about the houses listed, including address, city, price, number of bedrooms, and number of bathrooms. I also created an icon for each city represented (a total of three icons).

// House structure used for listing
typedef struct tagHOUSEINFO
{
char szAddress [MAX_ADDRESS];
char szCity [MAX_CITY];
int iPrice;
int iBeds;
int iBaths;
} HOUSEINFO;

HWND CreateListView (HWND hWndParent)
{
HWND hWndList; // handle to list view window
RECT rcl; // rectangle for setting size of window
HICON hIcon; // handle to an icon
int index; // index used in for loops
HIMAGELIST hSmall, hLarge; // handles to image lists for small and
// large icons
LV_COLUMN lvC; // list view column structure
char szText [MAX_PATH]; // place to store some text
LV_ITEM lvI; // list view item structure
int iSubItem; // index into column header string table

// Ensure that the common control DLL is loaded.
InitCommonControls ();

// Get the size and position of the parent window.
GetClientRect (hWndParent, &rcl);

// Create the list view window that starts out in details view
// and supports label editing.
hWndList = CreateWindowEx (
0L,
WC_LISTVIEW, // list view class
"", // no default text
WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_REPORT |
LVS_EDITLABELS | WS_EX_CLIENTEDGE, // styles
0, 0,
rcl.right - rcl.left, rcl.bottom - rcl.top,
hWndParent,
(HMENU)ID_LISTVIEW,
hInst,
NULL);

if (hWndList == NULL)
return NULL;

// Initialize the list view window.
// First initialize the image lists you will need:
// create image lists for the small and the large icons.

hSmall = ImageList_Create (BITMAP_WIDTH, BITMAP_HEIGHT,
FALSE, 3, 0);

hLarge = ImageList_Create (LG_BITMAP_WIDTH, LG_BITMAP_HEIGHT,
FALSE, 3, 0);

// Load the icons and add them to the image lists.
for (index = REDMOND; index <= SEATTLE; index++)
{
hIcon = LoadIcon (hInst, MAKEINTRESOURCE (index));
// You have 3 of each type of icon here, so add 3 at a time.
for (iSubItem = 0; iSubItem < 3; iSubItem++)
{
if ((ImageList_AddIcon (hSmall, hIcon) == -1) ||
(ImageList_AddIcon (hLarge, hIcon) == -1))
return NULL;
}
}

// Be sure that all the small icons were added.
if (ImageList_GetImageCount (hSmall) < 3)
return FALSE;

// Be sure that all the large icons were added.
if (ImageList_GetImageCount (hLarge) < 3)
return FALSE;

// Associate the image lists with the list view control.
ListView_SetImageList (hWndList, hSmall, LVSIL_SMALL);

ListView_SetImageList (hWndList, hLarge, LVSIL_NORMAL);
§

Next, after creating the list view control and then creating and initializing the image lists, it is time to add the column information. In order to do this, you must fill out an LV_COLUMN structure for each one of the columns and insert the columns by using the ListView_InsertColumn macro, as shown in the following code:

// Now initialize the columns you will need.
// Initialize the LV_COLUMN structure.
// The mask specifies that the fmt, width, pszText, and subitem members
// of the structure are valid.
lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvC.fmt = LVCFMT_LEFT; // left-align column
lvC.cx = 75; // width of column in pixels
lvC.pszText = szText;

// Add the columns.
for (index = 0; index <= NUM_COLUMNS; index++)
{
lvC.iSubItem = index;
LoadString (hInst, IDS_ADDRESS + index, szText, sizeof (szText));
if (ListView_InsertColumn (hWndList, index, &lvC) == -1)
return NULL;
}
§

After setting up the columns, add the items one by one. For each item, you must fill out an LV_ITEM structure. My samples include a callback function to provide the text for the items. Whenever the list view control needs the text for an item, my callback function is called.

// Finally, add the actual items to the control.
// Fill out the LV_ITEM structure for each item to add to the list.
// The mask specifies that the pszText, iImage, lParam, and state
// members of the LV_ITEM structure are valid.
lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
lvI.state = 0;
lvI.stateMask = 0;

for (index = 0; index < NUM_ITEMS; index++)
{
lvI.iItem = index;
lvI.iSubItem = 0;
// The parent window is responsible for storing the text.
// The list view control will send an LVN_GETDISPINFO
// when it needs the text to display.
lvI.pszText = LPSTR_TEXTCALLBACK;
lvI.cchTextMax = MAX_ITEMLEN;
lvI.iImage = index;
lvI.lParam = (LPARAM) &rgHouseInfo[index];

if (ListView_InsertItem (hWndList, &lvI) == -1)
return NULL;

for (iSubItem = 1; iSubItem < NUM_COLUMNS; iSubItem++)
{
ListView_SetItemText (hWndList, index, iSubItem,
LPSTR_TEXTCALLBACK);
}
}

return (hWndList);
}

Now that you've seen the code in C, you might wonder whether you need to do anything different or special in order to create and use a list view control in an MFC application. Under MFC, the list view control is supported through the CListCtrl class. In my MFCLIST sample, I created the control in the view class. In the definition of this class, I included a member variable for my CListCtrl object and my two CImageList objects:

class CMfclistView : public CView
{
protected: // create from serialization only
CMfclistView ();
DECLARE_DYNCREATE (CMfclistView);
CListCtrl m_ListCtl;
CImageList m_ImageLarge;
CImageList m_ImageSmall;
§

Then I created a message map entry for the WM_CREATE message and used the Create member function to create the list view control. This code looks nearly identical to the C code, as you can see here:

int CMfclistView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
int index;
int iSubItem;
HICON hIcon;
LV_COLUMN lvC; // list view column structure
static char szText [256]; // place to store some text
LV_ITEM lvI; // list view item structure

if (CView::OnCreate (lpCreateStruct) == -1)
return -1;

// Create the CListCtrl window.
m_ListCtl.Create (
WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_REPORT | LVS_EDITLABELS,
CRect (0, 0, 0, 0), // bounding rectangle
this, // parent
ID_LISTVIEW); // ID

// Create the large icon image list.
m_ImageLarge.Create (
LARGE_BITMAP_WIDTH,
LARGE_BITMAP_HEIGHT,
FALSE, // list does not include masks
NUM_BITMAPS,
0); // list won't grow

// Create the small icon image list.
m_ImageSmall.Create (
SMALL_BITMAP_WIDTH,
SMALL_BITMAP_HEIGHT,
FALSE, // list does not include masks
NUM_BITMAPS,
0); // list won't grow

// Load the icons and add them to the image lists.
for (index = IDI_BELLEVUE; index <= IDI_SEATTLE ; index++)
{
hIcon = ::LoadIcon (AfxGetResourceHandle (),
MAKEINTRESOURCE (index));
// You have 3 of each type of icon here, so add 3 at a time.
for (iSubItem = 0; iSubItem < 3; iSubItem++)
{
if ((m_ImageSmall.Add (hIcon) == -1) ||
(m_ImageLarge.Add (hIcon) == -1))
return NULL;
}
}

// Be sure that all the small icons were added.
if (m_ImageSmall.GetImageCount () < 3)
return NULL;

// Be sure that all the large icons were added.
if (m_ImageLarge.GetImageCount () < 3)
return NULL;

// Associate the image lists with the list view control.
m_ListCtl.SetImageList (&m_ImageSmall, LVSIL_SMALL);
m_ListCtl.SetImageList (&m_ImageLarge, LVSIL_NORMAL);

// Now initialize the columns you will need.
// Initialize the LV_COLUMN structure.
// The mask specifies that the fmt, width, pszText, and subitem members
// of the structure are valid.
lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvC.fmt = LVCFMT_LEFT; // left-align column
lvC.cx = 75; // width of column in pixels

// Add the columns.
for (index = 0; index <= NUM_COLUMNS; index++)
{
lvC.iSubItem = index;
lvC.pszText = szColumns [index];

if (m_ListCtl.InsertColumn (index, &lvC) == -1)
return NULL;
}

// Finally, add the actual items to the control.
// Fill out the LV_ITEM structure for each item to add to the list.
// The mask specifies that the pszText, iImage, lParam, and state
// members of the LV_ITEM structure are valid.
lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
lvI.state = 0;
lvI.stateMask = 0;

for (index = 0; index < NUM_ITEMS; index++)
{
lvI.iItem = index;
lvI.iSubItem = 0;
// The parent window is responsible for storing the text.
// The list view control will send an LVN_GETDISPINFO
// when it needs the text to display.
lvI.pszText = LPSTR_TEXTCALLBACK;
lvI.cchTextMax = MAX_ITEMLEN;
lvI.iImage = index;
lvI.lParam = (LPARAM) &rgHouseInfo[index];

if (m_ListCtl.InsertItem (&lvI) == -1)
return NULL;

for (iSubItem = 1; iSubItem < NUM_COLUMNS; iSubItem++)
{
m_ListCtl.SetItemText (index,
iSubItem,
LPSTR_TEXTCALLBACK);
}
}

return 0;
}