Figure 2   VListVw.c

 /**************************************************************************

   File: VListVw.c
   
**************************************************************************/
·
·
·
/**************************************************************************
   Global Variables
**************************************************************************/

HANDLE   g_hInst;
TCHAR    g_szClassName[] = TEXT("VListVwClass");

#define ITEM_COUNT   100000000
#define IDC_LISTVIEW 2000

/******************************************************************************

   WinMain

******************************************************************************/

int PASCAL WinMain(  HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
MSG  msg;
INITCOMMONCONTROLSEX iccex;

//required to use the common controls
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&iccex);

g_hInst = hInstance;

if(!hPrevInstance)
   if(!InitApplication(hInstance))
      return FALSE;

/* Perform initializations that apply to a specific instance */
if (!InitInstance(hInstance, nCmdShow))
   return FALSE;

/* Acquire and dispatch messages until a WM_QUIT uMessage is received. */
while(GetMessage( &msg, NULL, 0x00, 0x00))
   {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
   }

return msg.wParam;
}
·
·
·
/******************************************************************************

   CreateListView

******************************************************************************/

HWND CreateListView(HINSTANCE hInstance, HWND hwndParent)
{
DWORD       dwStyle;
HWND        hwndListView;
HIMAGELIST  himlSmall;
HIMAGELIST  himlLarge;
BOOL        bSuccess = TRUE;

dwStyle =   WS_TABSTOP | 
            WS_CHILD | 
            WS_BORDER | 
            WS_VISIBLE |
            LVS_AUTOARRANGE |
            LVS_REPORT | 
            LVS_OWNERDATA;
            
hwndListView = CreateWindowEx(   WS_EX_CLIENTEDGE,          // ex style
                                 WC_LISTVIEW, // class name defined in commctrl.h
                                 NULL,                      // window text
                                 dwStyle,                   // style
                                 0,                         // x position
                                 0,                         // y position
                                 0,                         // width
                                 0,                         // height
                                 hwndParent,                // parent
                                 (HMENU)IDC_LISTVIEW,       // ID
                                 g_hInst,                   // instance
                                 NULL);                     // no extra data

if(!hwndListView)
   return NULL;

ResizeListView(hwndListView, hwndParent);

//set the image lists
himlSmall = ImageList_Create(16, 16, ILC_COLORDDB | ILC_MASK, 1, 0);
himlLarge = ImageList_Create(32, 32, ILC_COLORDDB | ILC_MASK, 1, 0);

if (himlSmall && himlLarge)
   {
   HICON hIcon;

   //set up the small image list
   hIcon = LoadImage(g_hInst, MAKEINTRESOURCE(IDI_DISK), IMAGE_ICON, 16, 16,
                     LR_DEFAULTCOLOR);
   ImageList_AddIcon(himlSmall, hIcon);

   //set up the large image list
   hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_DISK));
   ImageList_AddIcon(himlLarge, hIcon);

   SendMessage(hwndListView, LVM_SETIMAGELIST, (WPARAM)LVSIL_SMALL,
               (LPARAM)himlSmall);
   SendMessage(hwndListView, LVM_SETIMAGELIST, (WPARAM)LVSIL_NORMAL,
               (LPARAM)himlLarge);
   }

return hwndListView;
}

/******************************************************************************

   ResizeListView

******************************************************************************/

void ResizeListView(HWND hwndListView, HWND hwndParent)
{
RECT  rc;

GetClientRect(hwndParent, &rc);

MoveWindow( hwndListView, 
            rc.left,
            rc.top,
            rc.right - rc.left,
            rc.bottom - rc.top,
            TRUE);

}

/******************************************************************************

   InitListView

******************************************************************************/

BOOL InitListView(HWND hwndListView)
{
LV_COLUMN   lvColumn;
int         i;
TCHAR       szString[5][20] = {  TEXT("Main Column"), 
                                 TEXT("Column 1"), 
                                 TEXT("Column 2"), 
                                 TEXT("Column 3"), 
                                 TEXT("Column 4")};

//initialize the columns
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 120;
for(i = 0; i < 5; i++)
   {
   lvColumn.pszText = szString[i];
   SendMessage(hwndListView, LVM_INSERTCOLUMN, (WPARAM)i, (LPARAM)&lvColumn);
   }

InsertListViewItems(hwndListView);

return TRUE;
}

/******************************************************************************

   InsertListViewItems

******************************************************************************/

BOOL InsertListViewItems(HWND hwndListView)
{
//empty the list
SendMessage(hwndListView, LVM_DELETEALLITEMS, 0, 0);

//set the number of items in the list
SendMessage(hwndListView, LVM_SETITEMCOUNT, (WPARAM)ITEM_COUNT,
            (LPARAM)LVSICF_NOINVALIDATEALL);

return TRUE;
}

/**************************************************************************

   ListViewNotify()

**************************************************************************/

LRESULT ListViewNotify(HWND hWnd, LPARAM lParam)
{
LPNMHDR  lpnmh = (LPNMHDR) lParam;
HWND     hwndListView = GetDlgItem(hWnd, IDC_LISTVIEW);

switch(lpnmh->code)
   {
   case LVN_GETDISPINFO:
      {
      LV_DISPINFO *lpdi = (LV_DISPINFO *)lParam;
      TCHAR szString[MAX_PATH];

      if(lpdi->item.iSubItem)
         {
         if(lpdi->item.mask & LVIF_TEXT)
            {
            wsprintf(szString, TEXT("Item %d - Column %d"), lpdi->item.iItem + 1,
                     lpdi->item.iSubItem);
            lstrcpyn(lpdi->item.pszText, szString, lpdi->item.cchTextMax);
            }
         }
      else
         {
         if(lpdi->item.mask & LVIF_TEXT)
            {
            wsprintf(szString, TEXT("Item %d"), lpdi->item.iItem + 1);
            lstrcpyn(lpdi->item.pszText, szString, lpdi->item.cchTextMax);
            }

         if(lpdi->item.mask & LVIF_IMAGE)
            {
            lpdi->item.iImage = 0;
            }

         if(lpdi->item.mask & LVIF_INDENT)
            {
            lpdi->item.iIndent = 0;
            }
         }
      }
      return 0;

   case LVN_ODCACHEHINT:
      {
      LPNMLVCACHEHINT   lpCacheHint = (LPNMLVCACHEHINT)lParam;
      /*
      This sample doesn't use this notification, but this is sent when the 
      ListView is about to ask for a range of items. On this notification, 
      you should load the specified items into your local cache. It is still 
      possible to get an LVN_GETDISPINFO for an item that has not been cached, 
      therefore, your application must take into account the chance of this 
      occurring.
      */
      }
      return 0;

   case LVN_ODFINDITEM:
      {
      LPNMLVFINDITEM lpFindItem = (LPNMLVFINDITEM)lParam;
      /*
      This sample doesn't use this notification, but this is sent when the 
      ListView needs a particular item. Return -1 if the item is not found.
      */
      }
      return 0;
   }

return 0;
}

/**************************************************************************

   SwitchView()

**************************************************************************/

void SwitchView(HWND hwndListView, DWORD dwView)
{
DWORD dwStyle = GetWindowLong(hwndListView, GWL_STYLE);

SetWindowLong(hwndListView, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | dwView);
ResizeListView(hwndListView, GetParent(hwndListView));
}
·
·
·
/**************************************************************************

   HandleContextMenu()

**************************************************************************/

BOOL HandleContextMenu( HWND hWnd, 
                        WPARAM wParam, 
                        LPARAM lParam)
{
HMENU hMenuLoad,
      hMenu;

if((HWND)wParam != GetDlgItem(hWnd, IDC_LISTVIEW))
   return FALSE;

hMenuLoad = LoadMenu(g_hInst, MAKEINTRESOURCE(IDM_CONTEXT_MENU));
hMenu = GetSubMenu(hMenuLoad, 0);

UpdateMenu(GetDlgItem(hWnd, IDC_LISTVIEW), hMenu);

TrackPopupMenu(   hMenu,
                  TPM_LEFTALIGN | TPM_RIGHTBUTTON,
                  LOWORD(lParam),
                  HIWORD(lParam),
                  0,
                  hWnd,
                  NULL);

DestroyMenu(hMenuLoad);

return TRUE;
}
·
·
·

Figure 3   ListView Extended Styles

Style Flag

Description

LVS_EX_CHECKBOXES

The control supplies check boxes for each item. You can retrieve the state of thecheck box by using the ListView_GetCheckState macro.

LVS_EX_FULLROWSELECT

When an item is selected, all of its subitems are also displayed as selected. Clicking on any subitem will select the entire row. This extended style is only effective in conjunction with the LVS_REPORT style.

LVS_EX_GRIDLINES

Dashed gridlines are displayed around all items and subitems. This extended style is only effective in conjunction with the LVS_REPORT style.

LVS_EX_HEADERDRAGDROP

Enables drag-and-drop re-ordering of the columns in the ListView. This extended style is only effective in conjunction with the LVS_REPORT style.

LVS_EX_SUBITEMIMAGES

Allows images to be displayed for subitems. This extended style is only effective in conjunction with the LVS_REPORT style.

LVS_EX_TRACKSELECT

Enables hot tracking of items in a ListView control. Hot Tracking, also known as Hover Selection, means that an item is automatically selected when the mouse pointer is over it for more than 1 second. This style applies to all styles of the ListView control.

Figure 4   New ListView Messages

Message

Description

wParam

lParam

Return Value

LVM_GETEXTENDEDLISTVIEWSTYLES

Retrieves the extended ListView styles.

Not used.

Not used.

A DWORD value which contains the extended style flags currently set in the control.

LVM_SETEXTENDEDLISTVIEWSTYLES

Sets the extended ListView styles.

Not used.

A DWORD value which contains the extended style flags to be set.

Not used.

LVM_GETCOLUMNORDERARRAY

Retrieves the current column-ordering information.

An integer value that indicates the number of columns in the control.

A pointer to an array of integers that receives the column-ordering information. The ordering information is given as the columns are displayed left-to-right. This array must be at least <number of columns> * sizeof(int).

A BOOL value indicating the success or failure of the message.

LVM_SETCOLUMNORDERARRAY

Sets the column-ordering information.

An integer value indicating the number of elements pointed to by the lParam.

A pointer to an array of integers that contains the new column-ordering information. The ordering information is given as the columns are displayed left-to-right.

A BOOL value indicating the success or failure of the message.

LVM_GETSUBITEMRECT

Retrieves the bounding rectangle of an item's subitem.

Index of the subitem's parent item.

Pointer to a RECT structure. Before sending the message, the one-based index of the subitem of interest is placed in the top member of the RECT structure and one of the following flags is placed in the left member of the RECT structure. After themessage is sent, the RECT structure will contain the desired information.
LVIR_BOUNDS - Returns the bounding rectangle of the entire subitem, including the icon and label.
LVIR_ICON - Returns the bounding rectangle of the subitem's icon.
LVIR_LABEL - Returns the bounding rectangle of the subitem's label.

A BOOL value indicating the success or failure of the message.

LVM_SUBITEMHITTEST

Determines what item or subitem, if any, is at the given point.

Not used.

Pointer to an LVHITTESTINFO structure.Before sending the message, the POINT member of the LVHITTESTINFO structure must contain the client coordinates of the point to be tested. After sending the message, if the hit test is successful and the point falls on an item, the iItem member of the LVHITTESTINFO structure will contain the index of the item on which the point falls. If the point falls on a subitem, the iSubItem member will contain the one-based index of the subitem on which the point falls andthe the iItem member will contain the subitem's parent index.

The index of the item or subitem if the hit test is successful. If the hit test fails, the subitem return value will be -1.

LVM_SETICONSPACING

Sets the icon spacing for a ListView which has the LVS_ICON style.

Not used.

A DWORD value that contains the new horizontal spacing (cx) in the low word and the new vertical spacing (cy) in the high word.

A DWORD value that contains the previous icon spacing. The previous horizontal spacing (cx) is in the low word and the previous vertical spacing (cy) is in the high word.

Figure 6   ListView.c

 /**************************************************************************

   File: ListView.c
   
**************************************************************************/
·
·
·
/**************************************************************************
   Global Variables
**************************************************************************/

HANDLE   g_hInst;
TCHAR    szClassName[] = TEXT("ListViewClass");
BOOL     g_bCustomDraw;

#define IDC_LISTVIEW 1000

/******************************************************************************

   WinMain

******************************************************************************/

int PASCAL WinMain(  HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
MSG  msg;
INITCOMMONCONTROLSEX iccex;

//required to use the common controls
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&iccex);

g_hInst = hInstance;

if(!hPrevInstance)
   if(!InitApplication(hInstance))
      return FALSE;

/* Perform initializations that apply to a specific instance */

if (!InitInstance(hInstance, nCmdShow))
   return FALSE;

/* Acquire and dispatch messages until a WM_QUIT uMessage is received. */

while(GetMessage(&msg, NULL, 0x00, 0x00))
   {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
   }

return msg.wParam;
}

·
·
·
/******************************************************************************

   MainWndProc

******************************************************************************/

LRESULT CALLBACK MainWndProc( HWND hWnd,
                              UINT uMessage,
                              WPARAM wParam,
                              LPARAM lParam)
{
switch (uMessage)
   {
   case WM_CREATE:
      {
      HWND  hwndListView;
      
      g_bCustomDraw = FALSE;
      
      // create the TreeView control
      hwndListView = CreateListView(g_hInst, hWnd);
      
      //initialize the TreeView control
      InitListView(hwndListView);
      }
      break;

   case WM_NOTIFY:
      return ListViewNotify(hWnd, lParam);
   
   case WM_SIZE:
      ResizeListView(GetDlgItem(hWnd, IDC_LISTVIEW), hWnd);
      break;

   case WM_INITMENUPOPUP:
      UpdateMenu(GetDlgItem(hWnd, IDC_LISTVIEW), GetMenu(hWnd));
      break;
   
   case WM_CONTEXTMENU:
      if(HandleContextMenu(hWnd, wParam, lParam))
         return FALSE;
      break;
   
   case WM_COMMAND:
      return HandleCommand(hWnd, wParam, lParam);

   case WM_DESTROY:
      PostQuitMessage(0);
      break;

   default:
      break;
   }
return DefWindowProc(hWnd, uMessage, wParam, lParam);
}


/******************************************************************************

   AboutDlgProc

******************************************************************************/

BOOL CALLBACK AboutDlgProc(   HWND hDlg, 
                              UINT uMessage, 
                              WPARAM wParam, 
                              LPARAM lParam)
{
switch (uMessage)
   {
   case WM_INITDIALOG:
      return TRUE;
      
   case WM_COMMAND:
      switch(wParam)
         {
         case IDOK:
            EndDialog(hDlg, IDOK);
            break;

         case IDCANCEL:
            EndDialog(hDlg, IDOK);
            break;
         }
      return TRUE;
    } 
    
return FALSE;
}

/******************************************************************************

   CreateListView

******************************************************************************/

HWND CreateListView(HINSTANCE hInstance, HWND hwndParent)
{
DWORD       dwStyle;
HWND        hwndListView;
HIMAGELIST  himlSmall;
HIMAGELIST  himlLarge;
SHFILEINFO  sfi;
BOOL        bSuccess = TRUE;

dwStyle =   WS_TABSTOP | 
            WS_CHILD | 
            WS_BORDER | 
            LVS_AUTOARRANGE |
            LVS_REPORT | 
            LVS_EDITLABELS |
            LVS_SHAREIMAGELISTS |
            WS_VISIBLE;
            
hwndListView = CreateWindowEx(   WS_EX_CLIENTEDGE,          // ex style
                                 WC_LISTVIEW,  // class name defined in commctrl.h
                                 NULL,                      // window text
                                 dwStyle,                   // style
                                 0,                         // x position
                                 0,                         // y position
                                 0,                         // width
                                 0,                         // height
                                 hwndParent,                // parent
                                 (HMENU)IDC_LISTVIEW,       // ID
                                 g_hInst,                   // instance
                                 NULL);                     // no extra data

if(!hwndListView)
   return NULL;

ResizeListView(hwndListView, hwndParent);

//set the large and small icon image lists
himlSmall = (HIMAGELIST)SHGetFileInfo( TEXT("C:\\"), 
                                       0,
                                       &sfi, 
                                       sizeof(SHFILEINFO), 
                                       SHGFI_SYSICONINDEX | SHGFI_SMALLICON);

himlLarge = (HIMAGELIST)SHGetFileInfo( TEXT("C:\\"), 
                                       0,
                                       &sfi, 
                                       sizeof(SHFILEINFO), 
                                       SHGFI_SYSICONINDEX | SHGFI_LARGEICON);

if (himlSmall && himlLarge)
   {
   SendMessage(hwndListView, LVM_SETIMAGELIST, (WPARAM)LVSIL_SMALL,
               (LPARAM)himlSmall);
   SendMessage(hwndListView, LVM_SETIMAGELIST, (WPARAM)LVSIL_NORMAL,
               (LPARAM)himlLarge);
   }

return hwndListView;
}

/******************************************************************************

   ResizeListView

******************************************************************************/

void ResizeListView(HWND hwndListView, HWND hwndParent)
{
RECT  rc;

GetClientRect(hwndParent, &rc);

MoveWindow( hwndListView, 
            rc.left,
            rc.top,
            rc.right - rc.left,
            rc.bottom - rc.top,
            TRUE);

}

/******************************************************************************

   InitListView

******************************************************************************/

BOOL InitListView(HWND hwndListView)
{
LV_COLUMN   lvColumn;
int         i;
TCHAR       szString[5][20] = {  TEXT("Image Number"), 
                                 TEXT("Left"), 
                                 TEXT("Top"), 
                                 TEXT("Right"), 
                                 TEXT("Bottom")};

//initialize the columns
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = 100;
for(i = 0; i < 5; i++)
   {
   //make the secondary columns smaller
   if(i)
      lvColumn.cx = 50;

   lvColumn.pszText = szString[i];
   SendMessage(hwndListView, LVM_INSERTCOLUMN, (WPARAM)i, (LPARAM)&lvColumn);
   }

InsertListViewItems(hwndListView);

return TRUE;
}

/******************************************************************************

   InsertListViewItems

******************************************************************************/

BOOL InsertListViewItems(HWND hwndListView)
{
LV_ITEM     lvItem;
int         i,
            nIndex,
            nImageCount;
TCHAR       szString[MAX_PATH];
HIMAGELIST  himl;
IMAGEINFO   ii;

SendMessage(hwndListView, WM_SETREDRAW, FALSE, 0);

//empty the list
SendMessage(hwndListView, LVM_DELETEALLITEMS, 0, 0);

//get the number of icons in the image list
himl = (HIMAGELIST)SendMessage(hwndListView, LVM_GETIMAGELIST,
                               (WPARAM)LVSIL_SMALL, 0);
nImageCount = ImageList_GetImageCount(himl);

for(i = 0; i < nImageCount; i++)
   {
   wsprintf(szString, TEXT("Image #%d"), i);
   
   //fill in the LV_ITEM structure for the first item
   lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
   lvItem.pszText = szString;
   lvItem.iImage = i;
   lvItem.iItem = SendMessage(hwndListView, LVM_GETITEMCOUNT, 0, 0);
   lvItem.iSubItem = 0;

   //add the item - get the index in case the ListView is sorted
   nIndex = SendMessage(hwndListView, LVM_INSERTITEM, (WPARAM)0, (LPARAM)&lvItem);

   //set the text and images for the sub-items
   ImageList_GetImageInfo(himl, i, &ii);
   wsprintf(szString, TEXT("%d"), ii.rcImage.left);
   lvItem.iSubItem = 1;
   SendMessage(hwndListView, LVM_SETITEM, 0, (LPARAM)&lvItem);

   wsprintf(szString, TEXT("%d"), ii.rcImage.top);
   lvItem.iSubItem = 2;
   SendMessage(hwndListView, LVM_SETITEM, 0, (LPARAM)&lvItem);

   wsprintf(szString, TEXT("%d"), ii.rcImage.right);
   lvItem.iSubItem = 3;
   SendMessage(hwndListView, LVM_SETITEM, 0, (LPARAM)&lvItem);

   wsprintf(szString, TEXT("%d"), ii.rcImage.bottom);
   lvItem.iSubItem = 4;
   SendMessage(hwndListView, LVM_SETITEM, 0, (LPARAM)&lvItem);
   }

SendMessage(hwndListView, WM_SETREDRAW, TRUE, 0);
UpdateWindow(hwndListView);

return TRUE;
}

/**************************************************************************

   ListViewNotify()

**************************************************************************/

LRESULT ListViewNotify(HWND hWnd, LPARAM lParam)
{
LPNMHDR  lpnmh = (LPNMHDR) lParam;
HWND     hwndListView = GetDlgItem(hWnd, IDC_LISTVIEW);

switch(lpnmh->code)
   {
   case NM_CUSTOMDRAW:
      {
      LPNMLVCUSTOMDRAW  lplvcd = (LPNMLVCUSTOMDRAW)lParam;

      /*
      CDDS_PREPAINT is sent when the control is about to paint itself. You 
      implement custom draw by returning the proper value to this 
      notification.
      */
      if(lplvcd->nmcd.dwDrawStage == CDDS_PREPAINT)
         {
         if(g_bCustomDraw)
            {
            //tell the control we want pre-paint notifications for each item
            return CDRF_NOTIFYITEMDRAW;
            }
         //tell the control that we won't be doing any custom drawing
         return CDRF_DODEFAULT;
         }

      /*
      CDDS_ITEMPREPAINT is sent when the control is about to paint an item. 
      You will only get these if you returned CDRF_NOTIFYITEMDRAW in 
      response to CDDS_PREPAINT.
      */
      if(lplvcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
         {
         LRESULT  lReturn = CDRF_DODEFAULT;
         /*
         For the ListView, the index of the item being drawn is stored in the 
         dwItemSpec member of the NMCUSTOMDRAW structure. In this example, only 
         the odd items will be drawn using the bold font.
         */
         if(lplvcd->nmcd.dwItemSpec & 0x01)
            {
            HFONT    hFont;
            LOGFONT  lf;

            //get the existing font
            hFont = (HFONT)SendMessage(hwndListView, WM_GETFONT, 0, 0);

            //now get the font's information
            GetObject(hFont, sizeof(lf), &lf);

            //make this font bold
            lf.lfWeight = FW_BOLD;

            //create the new font
            hFont = CreateFontIndirect(&lf);

            /*
            To change the font, just select the desired font into the HDC 
            provided.
            */
            SelectObject(lplvcd->nmcd.hdc, hFont);

            /*
            To change the text and background colors in a ListView, set the 
            clrText and clrTextBk members of the NMLVCUSTOMDRAW structure to 
            the desired color. This is different than most other controls that 
            support custom draw. To change the text and background colors in 
            the others, you just call SetTextColor and SetBkColor on the HDC 
            provided. 
            */
            lplvcd->clrText = RGB(255, 0, 0);
            lplvcd->clrTextBk = RGB(255, 255, 255);

            /*
            If you change the font, return CDRF_NEWFONT so the control can 
            recalculate the extent of the text. Returning CDRF_NOTIFYPOSTPAINT 
            causes the control to send us notifications with CDDS_ITEMPOSTPAINT.
            */
            lReturn = CDRF_NEWFONT | CDRF_NOTIFYPOSTPAINT;
            }
         else
            {
            lplvcd->clrText = RGB(0, 0, 255);
            lplvcd->clrTextBk = RGB(255, 255, 255);
            }

         return lReturn;
         }

      if(lplvcd->nmcd.dwDrawStage == CDDS_ITEMPOSTPAINT)
         {
         HFONT hFont = GetStockObject(DEFAULT_GUI_FONT);

         //clean up stuff here
         hFont = SelectObject(lplvcd->nmcd.hdc, hFont);

         DeleteFont(hFont);
         
         return CDRF_DODEFAULT;
         }
      }
      return CDRF_DODEFAULT;
   
   }

return 0;
}

/**************************************************************************

   SwitchView()

**************************************************************************/

void SwitchView(HWND hwndListView, DWORD dwView)
{
DWORD dwStyle = GetWindowLong(hwndListView, GWL_STYLE);

SetWindowLong(hwndListView, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | dwView);
ResizeListView(hwndListView, GetParent(hwndListView));
}

/**************************************************************************

   AddExStyle()

**************************************************************************/

void AddExStyle(HWND hwndListView, DWORD dwNewStyle)
{
DWORD dwStyle = SendMessage(hwndListView, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
dwStyle |= dwNewStyle;
SendMessage(hwndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
}

/**************************************************************************

   RemoveExStyle()

**************************************************************************/

void RemoveExStyle(HWND hwndListView, DWORD dwNewStyle)
{
DWORD dwStyle = SendMessage(hwndListView, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
dwStyle &= ~dwNewStyle;
SendMessage(hwndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
}

/**************************************************************************

   HandleCommand()

**************************************************************************/

LRESULT HandleCommand(  HWND hWnd, 
                        WPARAM wParam, 
                        LPARAM lParam)
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
   {
   case IDM_LARGEICONS:
      SwitchView(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_ICON);
      break;
   
   case IDM_SMALLICONS:
      SwitchView(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_SMALLICON);
      break;
   
   case IDM_LIST:
      SwitchView(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_LIST);
      break;
   
   case IDM_REPORT:
      SwitchView(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_REPORT);
      break;
   
   case IDM_EXIT:
      DestroyWindow(hWnd);
      break;
   
   case IDM_ABOUT:
      DialogBox(g_hInst, MAKEINTRESOURCE(IDD_ABOUT), hWnd, AboutDlgProc);
      break;   

   case IDM_INDENT:
      {
      LV_ITEM  lvItem;
      int      i,
               nItemCount;

      ZeroMemory(&lvItem, sizeof(lvItem));

      nItemCount = (int)SendDlgItemMessage(hWnd, IDC_LISTVIEW, LVM_GETITEMCOUNT,
                                           0, 0);

      //run through the list, indenting the selected items
      for(i = 0; i < nItemCount; i++)
         {
         lvItem.mask = LVIF_STATE | LVIF_INDENT;
         lvItem.iItem = i;
         lvItem.stateMask = LVIS_SELECTED;
         SendDlgItemMessage(hWnd, IDC_LISTVIEW, LVM_GETITEM, 0, (LPARAM)&lvItem);

         if(lvItem.state & LVIS_SELECTED)
            {
            lvItem.mask = LVIF_INDENT;
            lvItem.iIndent++;
            SendDlgItemMessage(hWnd, IDC_LISTVIEW, LVM_SETITEM, 0,
                               (LPARAM)&lvItem);
            }
         }
      }
      break;
   
   case IDM_UNINDENT:
      {
      LV_ITEM  lvItem;
      int      i,
               nItemCount;

      ZeroMemory(&lvItem, sizeof(lvItem));

      nItemCount = (int)SendDlgItemMessage(hWnd, IDC_LISTVIEW, LVM_GETITEMCOUNT,
                                           0, 0);

      //run through the list, unindenting the selected items
      for(i = 0; i < nItemCount; i++)
         {
         lvItem.mask = LVIF_STATE | LVIF_INDENT;
         lvItem.iItem = i;
         lvItem.stateMask = LVIS_SELECTED;
         SendDlgItemMessage(hWnd, IDC_LISTVIEW, LVM_GETITEM, 0, (LPARAM)&lvItem);

         if(lvItem.state & LVIS_SELECTED)
            {
            lvItem.mask = LVIF_INDENT;
            if(lvItem.iIndent)
               {
               lvItem.iIndent--;
               SendDlgItemMessage(hWnd, IDC_LISTVIEW, LVM_SETITEM, 0,
                                  (LPARAM)&lvItem);
               }
            }
         }
      }
      break;
   
   case IDM_CUSTOMDRAW:
      //toggle the custom draw flag
      g_bCustomDraw = !g_bCustomDraw;

      //force the control to redraw itself
      InvalidateRect(GetDlgItem(hWnd, IDC_LISTVIEW), NULL, TRUE);
      break;
   
   case IDM_CHECKBOXES:
      if(LVS_EX_CHECKBOXES & 
         (DWORD)SendDlgItemMessage(hWnd, IDC_LISTVIEW,
                                   LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0))
         RemoveExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_CHECKBOXES);
      else
         AddExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_CHECKBOXES);
      break;

   case IDM_HOVERSELECT:
      if(LVS_EX_TRACKSELECT & 
         (DWORD)SendDlgItemMessage(hWnd, IDC_LISTVIEW,
                                   LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0))
         RemoveExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_TRACKSELECT);
      else
         AddExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_TRACKSELECT);
      break;

   case IDM_GRIDLINES:
      if(LVS_EX_GRIDLINES & 
         (DWORD)SendDlgItemMessage(hWnd, IDC_LISTVIEW,
                                   LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0))
         RemoveExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_GRIDLINES);
      else
         AddExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_GRIDLINES);
      break;

   case IDM_FULLROWSELECT:
      if(LVS_EX_FULLROWSELECT &
         (DWORD)SendDlgItemMessage(hWnd, IDC_LISTVIEW,
                                   LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0))
         RemoveExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_FULLROWSELECT);
      else
         AddExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_FULLROWSELECT);
      break;

   case IDM_HEADERDRAGDROP:
      if(LVS_EX_HEADERDRAGDROP &
         (DWORD)SendDlgItemMessage(hWnd, IDC_LISTVIEW,
                                   LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0))
         RemoveExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_HEADERDRAGDROP);
      else
         AddExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_HEADERDRAGDROP);
      break;

   case IDM_SUBITEMIMAGES:
      if(LVS_EX_SUBITEMIMAGES & 
         (DWORD)SendDlgItemMessage(hWnd, IDC_LISTVIEW,
                                   LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0))
         RemoveExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_SUBITEMIMAGES);
      else
         AddExStyle(GetDlgItem(hWnd, IDC_LISTVIEW), LVS_EX_SUBITEMIMAGES);
      
      InvalidateRect(GetDlgItem(hWnd, IDC_LISTVIEW), NULL, TRUE);
      UpdateWindow(GetDlgItem(hWnd, IDC_LISTVIEW));
      break;

   }

return 0;
}

/**************************************************************************
   HandleContextMenu()
**************************************************************************/
BOOL HandleContextMenu( HWND hWnd, 
                        WPARAM wParam, 
                        LPARAM lParam)
{
HMENU hMenuLoad,
      hMenu;

if((HWND)wParam != GetDlgItem(hWnd, IDC_LISTVIEW))
   return FALSE;

hMenuLoad = LoadMenu(g_hInst, MAKEINTRESOURCE(IDM_CONTEXT_MENU));
hMenu = GetSubMenu(hMenuLoad, 0);

UpdateMenu(GetDlgItem(hWnd, IDC_LISTVIEW), hMenu);

TrackPopupMenu(   hMenu,
                  TPM_LEFTALIGN | TPM_RIGHTBUTTON,
                  LOWORD(lParam),
                  HIWORD(lParam),
                  0,
                  hWnd,
                  NULL);

DestroyMenu(hMenuLoad);

return TRUE;
}

/**************************************************************************

   UpdateMenu()

**************************************************************************/
void UpdateMenu(HWND hwndListView, HMENU hMenu)
{
UINT  uID;
DWORD dwStyle;

//uncheck all of these guys
CheckMenuItem(hMenu, IDM_LARGEICONS,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_SMALLICONS,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_LIST,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_REPORT,  MF_BYCOMMAND | MF_UNCHECKED);

CheckMenuItem(hMenu, IDM_CUSTOMDRAW,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_CHECKBOXES,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_HOVERSELECT,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_GRIDLINES,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_FULLROWSELECT,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_HEADERDRAGDROP,  MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, IDM_SUBITEMIMAGES,  MF_BYCOMMAND | MF_UNCHECKED);

//check the appropriate view menu item
dwStyle = GetWindowLong(hwndListView, GWL_STYLE);
switch(dwStyle & LVS_TYPEMASK)
   {
   case LVS_ICON:
      uID = IDM_LARGEICONS;
      break;
      
   case LVS_SMALLICON:
      uID = IDM_SMALLICONS;
      break;
      
   case LVS_LIST:
      uID = IDM_LIST;
      break;
   
   case LVS_REPORT:
      uID = IDM_REPORT;
      break;
   }
CheckMenuRadioItem(hMenu, IDM_LARGEICONS, IDM_REPORT, uID,  MF_BYCOMMAND |
                   MF_CHECKED);

//check the appropriate extended style items
dwStyle = (DWORD)SendMessage(hwndListView, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);

//only update and enable grid lines, full row select, header drag drop, and 
//sub-item images items if in report view
if(uID == IDM_REPORT)
   {
   EnableMenuItem(hMenu, IDM_GRIDLINES, MF_BYCOMMAND | MF_ENABLED);
   EnableMenuItem(hMenu, IDM_FULLROWSELECT, MF_BYCOMMAND | MF_ENABLED);
   EnableMenuItem(hMenu, IDM_HEADERDRAGDROP, MF_BYCOMMAND | MF_ENABLED);
   EnableMenuItem(hMenu, IDM_SUBITEMIMAGES, MF_BYCOMMAND | MF_ENABLED);

   //can we indent or unindent?
   if(SendMessage(hwndListView, LVM_GETSELECTEDCOUNT, 0, 0))
      {
      EnableMenuItem(hMenu, IDM_INDENT, MF_BYCOMMAND | MF_ENABLED);
      EnableMenuItem(hMenu, IDM_UNINDENT, MF_BYCOMMAND | MF_ENABLED);
      }
   else
      {
      EnableMenuItem(hMenu, IDM_INDENT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
      EnableMenuItem(hMenu, IDM_UNINDENT, MF_BYCOMMAND | MF_DISABLED |
                     MF_GRAYED);
      }

   if(dwStyle & LVS_EX_GRIDLINES)
      {
      CheckMenuItem(hMenu, IDM_GRIDLINES, MF_BYCOMMAND | MF_CHECKED);
      }

   if(dwStyle & LVS_EX_FULLROWSELECT)
      {
      CheckMenuItem(hMenu, IDM_FULLROWSELECT, MF_BYCOMMAND | MF_CHECKED);
      }

   if(dwStyle & LVS_EX_HEADERDRAGDROP)
      {
      CheckMenuItem(hMenu, IDM_HEADERDRAGDROP, MF_BYCOMMAND | MF_CHECKED);
      }

   if(dwStyle & LVS_EX_SUBITEMIMAGES)
      {
      CheckMenuItem(hMenu, IDM_SUBITEMIMAGES, MF_BYCOMMAND | MF_CHECKED);
      }
   }
else
   {
   EnableMenuItem(hMenu, IDM_INDENT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
   EnableMenuItem(hMenu, IDM_UNINDENT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
   EnableMenuItem(hMenu, IDM_GRIDLINES, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
   EnableMenuItem(hMenu, IDM_FULLROWSELECT, MF_BYCOMMAND | MF_DISABLED |
                  MF_GRAYED);
   EnableMenuItem(hMenu, IDM_HEADERDRAGDROP, MF_BYCOMMAND | MF_DISABLED |
                  MF_GRAYED);
   EnableMenuItem(hMenu, IDM_SUBITEMIMAGES, MF_BYCOMMAND | MF_DISABLED |
                  MF_GRAYED);
   }

//is custom drawing turned on?
if(g_bCustomDraw)
   {
   CheckMenuItem(hMenu, IDM_CUSTOMDRAW,  MF_BYCOMMAND | MF_CHECKED);
   }

//are checkboxes turned on?
if(dwStyle & LVS_EX_CHECKBOXES)
   {
   CheckMenuItem(hMenu, IDM_CHECKBOXES,  MF_BYCOMMAND | MF_CHECKED);
   }

//is hover select turned on?
if(dwStyle & LVS_EX_TRACKSELECT )
   {
   CheckMenuItem(hMenu, IDM_HOVERSELECT,  MF_BYCOMMAND | MF_CHECKED);
   }

}

Figure 7   HDITEM Structure

Member

Description

UINT mask

Specifies which of the members contain valid information. Or, when retrieving information, which members are being requested. In addition to the mask values that are defined for the HD_ITEM structure, there are two new mask flags: HDI_IMAGE - The iImage member is valid and specifies the image to be displayed with the item. HDI_ORDER - The iOrder member is valid and specifies the order value for the item.

int cxy

Width or height of the item.

LPTSTR pszText

Pointer to a zero-terminated string that specifies the text for the item. If this member is set to LPSTR_TEXTCALLBACK, the control will request text information for this item by using HDN_GETDISPINFO notification messages.

HBITMAP hbm

Handle to the bitmap that should be displayed with the item.

int cchTextMax

Length of the item text in characters.

int fmt

Set of bit flags that specify the item's format. These are the same as the format flags defined for the HD_ITEM structure.

LPARAM lParam

32-bit application-defined value.

int iImage

Zero-based index of an image within the image list. The specified image will be displayed with the header item, but does not take the place of the one specified in the hbm field. If iImage is set to I_IMAGECALLBACK, the control will request the image information for this item by using HDN_GETDISPINFO notification messages.

int iOrder

Specifies the order in which the item appears within the header control, from left to right. That is, the value for the far left item is 0, the value for the next item to the right is 1, and so on.

Figure 8   NMHDDISPINFO Stucture

Member

Description

NMHDR hdr

An NMHDR structure containing information about the notification message.

int iItem

The zero-based index of the item in the header control.

UINT mask

A set of bit flags specifying which members of the structure are being requested by the control. This value can be a combination of the following values:

HDI_TEXT - The pszText member must be filled in.

HDI_IMAGE - The iImage member must be filled in.

HDI_LPARAM - The lParam member must be filled in.

HDI_DI_SETITEM - If you add this flag to the mask member before returning from the notification, this specifies that the header control should store the item information and not ask for it again.

LPTSTR pszText

Pointer to a zero-terminated string that contains the text that will be displayed for the header item. When processing the notification, you should copy the string to this location, making sure not to copy more than cchTextMax characters into the buffer.

int cchTextMax

Size of the buffer pointed to by pszText.

int iImage

Zero-based index of the image that should be displayed with the item.

LPARAM lParam

32-bit application-defined data.