Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using ListView control under Win32 API

0.00/5 (No votes)
2 Aug 2003 16  
This article will show/teach how to use ListView under Win32 API (Visual C++ 6.0).

Introduction

This article is for those who did not find, any useful code examples and information regarding ListControl (ListView). For me, it was really hard to get a working code for Win32 API, since I don't code in MFC. The only help I was able to get was win32.hlp which contains every API by Microsoft. Note that it did not have any examples in it. Also searching through forums can be helpful, but it is slow and sometimes you really wanna get some stuff going fast and clear. I decided to write this article for the coders who don't know any MFC (class) or just starting with ListControl via APIs. First, I must say that all examples I've seen so far lacked information for mon-MFC coders!. If you can't code or can't understand MFC, and are using the API, then this little article is for you.

So let's start.

#include <commctrl.h>

Add it into your C/CPP file so we can use its functions. We add a ListControl to our dialog (you can also create it via the CreateWindowEx API!). Once we named it, (IDC_LIST), we add 2 buttons which will do something to our ListView. Once we've done it, we need to save its HANDLE (which is HWND). We use a static HWND hList=NULL; as a global variable (so we can access it in any function). Now we need to initialize our ListView (I will do it in the WM_INITDIALOG message). First we get the handle: hList=GetDlgItem(hWnd,IDC_LIST);, then we add a LVITEM LvItem; struct (also a global variable). The LVITEM struct consists of a few parameters:

typedef struct _LV_ITEM {  
    UINT   mask;        // attributes of this data structure
    int    iItem;       // index of the item to which this structure refers
    int    iSubItem;    // index of the subitem to which this structure refers
    UINT   state;       // Specifies the current state of the item
    UINT   stateMask;   // Specifies the bits of the state member that are valid. 
    LPTSTR  pszText;    // Pointer to a null-terminated string
                        // that contains the item text 
    int    cchTextMax;  // Size of the buffer pointed to by the pszText member
    int    iImage;      // index of the list view item's icon 
    LPARAM lParam;      // 32-bit value to associate with item 
} LV_ITEM;

We also add a LVCOLUMN LvCol; (global variable). The LVCOLUMN struct consists of a few parameters:

typedef struct _LV_COLUMN {  
    UINT mask;       // which members of this structure contain valid information
    int fmt;         // alignment of the column heading and the subitem text 
    int cx;          // Specifies the width, in pixels, of the column.
    LPTSTR pszText;  // Pointer to a null-terminated string
                     // that contains the column heading 
    int cchTextMax;  // Specifies the size, in characters, of the buffer
    int iSubItem;    // index of subitem
} LV_COLUMN;

Now that we know about the struct's members, let's initialize and use what we need: at the WM_INITDIALOG message.

// Here we put the info on the Coulom headers
// this is not data, only name of each header we like
Memset(&LvCol,0,sizeof(LvCol));                  // Zero Members
LvCol.mask=LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM;    // Type of mask
LvCol.cx=0x28;                                   // width between each coloum
LvCol.pszText="Item";                            // First Header Text
LvCol.cx=0x42;                                   // width of column

For more information that our list control can supply us, we can use the extended styles (full raw select):

SendMessage(hList,LVM_SETEXTENDEDLISTVIEWSTYLE,
           0,LVS_EX_FULLROWSELECT); // Set style

Now, let us put some columns (as much as you like):

// Inserting Couloms as much as we want
SendMessage(hList,LVM_INSERTCOLUMN,0,(LPARAM)&LvCol); // Insert/Show the coloum
LvCol.pszText="Sub Item1";                            // Next coloum
SendMessage(hList,LVM_INSERTCOLUMN,1,(LPARAM)&LvCol); // ...
LvCol.pszText="Sub Item2";                            //
SendMessage(hList,LVM_INSERTCOLUMN,2,(LPARAM)&LvCol); //
LvCol.pszText="Sub Item3";                            //
SendMessage(hList,LVM_INSERTCOLUMN,3,(LPARAM)&LvCol); //
LvCol.pszText="Sub Item4";                            //
SendMessage(hList,LVM_INSERTCOLUMN,4,(LPARAM)&LvCol); //
LvCol.pszText="Sub Item5";                            //
SendMessage(hList,LVM_INSERTCOLUMN,5,(LPARAM)&LvCol); // ...same as above

Once we've added headers (columns) to our listview, we need to add some items to it. This is done via the above mentioned LVITEM struct.

memset(&LvItem,0,sizeof(LvItem)); // Zero struct's Members

//  Setting properties Of members:

LvItem.mask=LVIF_TEXT;   // Text Style
LvItem.cchTextMax = 256; // Max size of test
LvItem.iItem=0;          // choose item  
LvItem.iSubItem=0;       // Put in first coluom
LvItem.pszText="Item 0"; // Text to display (can be from a char variable) (Items)

SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&LvItem); // Send info to the Listview

for(i=1;i<=5;i++) // Add SubItems in a loop
{
   LvItem.iSubItem=i;
   sprintf(Temp,"SubItem %d",i);
   LvItem.pszText=Temp;
   SendMessage(hList,LVM_SETITEM,0,(LPARAM)&LvItem); // Enter text to SubItems
}

How about inserting a new item?

// lets add a new Item:

LvItem.iItem=1;           // choose item  
LvItem.iSubItem=0;        // Put in first coluom
LvItem.pszText="Item 1";  // Text to display 

SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&LvItem); // Send to the Listview

for(i=1;i<=5;i++) // Add SubItems in a loop
{
  LvItem.iSubItem=i;
  sprintf(Temp,"SubItem %d",i);
  LvItem.pszText=Temp;
  SendMessage(hList,LVM_SETITEM,0,(LPARAM)&LvItem); // Enter text to SubItems
}

Note: Temp is a char variable: char Temp[255]="".

Adding an item via the button:

case IDC_ADDITEM:
{
   int iItem;
   char ItemText[100];

   iItem=SendMessage(hList,LVM_GETITEMCOUNT,0,0); // number of items
                           
   GetDlgItemText(hWnd,IDC_ADD,ItemText,100); // get text from editobx

   if((lstrlen(ItemText))==0)  // check if items exist
   {
     MessageBox(hWnd,"Please Write Some Text",
              "Error",MB_OK|MB_ICONINFORMATION);
     break;
   }

   LvItem.iItem=iItem;   // choose item to enter to 
   LvItem.iSubItem=0;    // Put in first coluom (no need subitems)

   // Text to display (can be from a char variable) (Items)
   LvItem.pszText=ItemText;
   // Send item text to the Listview
   SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&LvItem);
}
break;

Adding a Subitem via the button:

case IDC_ADDSUBITEM:
{
  int Item,i;
  char SubItemText[100];

  Item=SendMessage(hList,LVM_GETITEMCOUNT,0,0); // number of items

  GetDlgItemText(hWnd,IDC_ADDSUB,SubItemText,100); // get text from editobx

  if((lstrlen(SubItemText))==0)  // check if items exist 
  {
    MessageBox(hWnd,"Please Write Some Text", 
             "Error",MB_OK|MB_ICONINFORMATION);
    break;
  }

  // choose the item-1 (we can't add subitems to non existing item
  LvItem.iItem=Item-1;

  for(i=1;i<=5;i++)        // add 5 subitems
  {
    // Text to display (can be from a char variable) (Items)
    LvItem.pszText=SubItemText;
    // Put in coluom of index i
    LvItem.iSubItem=i;   

    SendMessage(hList,LVM_SETITEM,0,
      (LPARAM)&LvItem); // send Subitem text to listview
  }
}
break;

Put both item and SubItems.

case IDC_BOTH:
{
  int itemIndex,j;
  char iSubItemText[100]="";
  char iItemText[100]="";

  itemIndex=SendMessage(hList,LVM_GETITEMCOUNT,0,0);   // number of items

  GetDlgItemText(hWnd,IDC_ADD,iItemText,100);          // get text
  GetDlgItemText(hWnd,IDC_ADDSUB,iSubItemText,100);    // get text

  // we enter text to the editboxes?
  if((lstrlen(iSubItemText) && lstrlen(iItemText))==0) 
  {
    MessageBox(hWnd,"Please Write Some Text",
           "Error",MB_OK|MB_ICONINFORMATION);
    break;
  }

  LvItem.iItem=itemIndex;            // item will be put at itemIndex
  LvItem.iSubItem=0;                 // adding item, no need subitems
  LvItem.pszText=iItemText;          // set pointer to the item text
  SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&LvItem); // put it

  for(j=1;j<=5;j++) // add 5 subitems
  {
    LvItem.pszText=iSubItemText;   // Text to display
    LvItem.iSubItem=j;            // Put in coluom at index j

    SendMessage(hList,LVM_SETITEM,0,(LPARAM)&LvItem); // put it :)
  }
}
break;

Deleting all items at the list control is done via one message to the list control:

SendMessage(hList,LVM_DELETEALLITEMS,0,0);

Deleting Specific Selected Item in the ListView.

We use the flag (global variable) to help us notice when an item is focused or selected (has a blue color wrapping the item).

case IDC_DELSELITEM:
  if(flag) // we pressed an item?
    SendMessage(hList,LVM_DELETEITEM,iSelect,0); // delete the item selected

  flag=0; // reset the flag after we used the item
  break;

The next part is to show how a user selects an item and responds to the button or how do we use the mouse and affect the list view while clicking on it with double click, left click.. etc.

Affecting the ListView must be done via the WM_NOTIFY message, this is because the ListView doesn't have it over its properties like ListBox has! We put the WM_NOTIFY message under the switch(Message){...}.

case WM_NOTIFY: // the message that is being sent always
{
  switch(LOWORD(wParam)) // hit control
  {
    case IDC_LIST:      // did we hit our ListView contorl?

To make us select the items (we are using full raw select), we need to use NMHDR and its members:

// if code == NM_CLICK - Single click on an item
if(((LPNMHDR)lParam)->code == NM_CLICK) 
{
// do some stuff here
}

A list of code we can use for our listview are:

...
...
NM_CLICK      - pnmh = (NMHDR FAR *) lParam;
NM_DBLCLK     - pnmh = (NMHDR FAR *) lParam; 
NM_KILLFOCUS  - pnmh = (NMHDR FAR *) lParam; 
NM_RCLICK     - pnmh = (NMHDR FAR *) lParam; 
NM_RDBLCLK    - pnmh = (NMHDR FAR *) lParam;  
NM_RETURN     - pnmh = (NMHDR FAR *) lParam; 
NM_SETFOCUS   - pnmh = (NMHDR FAR *) lParam;
LVN_ENDLABELEDIT    - pdi = (LV_DISPINFO FAR *) lParam; 
LVN_BEGINLABELEDIT  -pdi = (LV_DISPINFO FAR *) lParam; 
...
...

See win32.hlp for more useful messages.

So, let's continue from what we left, we need to select an item in order to delete it. We are using the code we learned/used from above:

if(((LPNMHDR)lParam)->code == NM_CLICK)
{
  iSelect=SendMessage(hList,LVM_GETNEXTITEM,
        -1,LVNI_FOCUSED); // return item selected

  if(iSelect==-1) // no items
  {
    MessageBox(hWnd,"No Items in ListView",
           "Error",MB_OK|MB_ICONINFORMATION);
    break;
  }

  flag=1; // set flag so that we know there was a hit
}

Note: iSelect is a global variable.

Say we want to get the text from an item & subitems once we double click an item. We need to add a NM_DBLCLK message and process it with our code:

if(((LPNMHDR)lParam)->code == NM_DBLCLK)
{
//...
}

We use all the messages that the listview has to offer us and from the code we used above, it wont be hard to get the item/sub items:

case WM_NOTIFY:
{
  switch(LOWORD(wParam))
  {
    case IDC_LIST: 
    if(((LPNMHDR)lParam)->code == NM_DBLCLK)
    {
    char Text[255]={0};
    char Temp[255]={0};
    char Temp1[255]={0};
    int iSlected=0;
    int j=0;

    iSlected=SendMessage(hList,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);

    if(iSlected==-1)
    {
        MessageBox(hWnd,"No Items in ListView",
                 "Error",MB_OK|MB_ICONINFORMATION);
        break;
    }

    memset(&LvItem,0,sizeof(LvItem));
    LvItem.mask=LVIF_TEXT;
    LvItem.iSubItem=0;
    LvItem.pszText=Text;
    LvItem.cchTextMax=256;
    LvItem.iItem=iSlected;

    SendMessage(hList,LVM_GETITEMTEXT, 
                iSlected, (LPARAM)&LvItem); 

    sprintf(Temp1,Text);

    for(j=1;j<=5;j++)
    {
        LvItem.iSubItem=j;
        SendMessage(hList,LVM_GETITEMTEXT, 
                      iSlected, (LPARAM)&LvItem);
        sprintf(Temp," %s",Text);
        lstrcat(Temp1,Temp);
    }

    MessageBox(hWnd,Temp1,"test",MB_OK);

     }
   }
}

Label Editing

Say we want to edit our item's text at runtime, how can we do it? We will use the LVN_BEGINLABELEDIT and LVN_ENDLABELEDIT messages.

if(((LPNMHDR)lParam)->code == LVN_BEGINLABELEDIT)
{
   hEdit=ListView_GetEditControl(hList);
}

When we give the listbox an "edit labels" dialog property, we are able to edit the item's text when we press on it with the mouse [select item -> click small left button]. Once we did it, we need to trap the editbox created at run-time by the listbox. We use the LVN_BEGINLABELEDIT message to begin the label edit. Once it's trapped, we are saving the created edit's handle.

I used the ListView_GetEditControl macro because it is shorter to write :), but you can use the SendMessage as well. (Return a HWND if using HWND hEdit=(HWND)SendMessage().) We save the handle into hEdit.

Note: hEdit is a global variable!

Once we saved the handle, we need to use LVN_ENDLABELEDIT message to actually get the text from the created edit and save it over a text buffer.

if(((LPNMHDR)lParam)->code == LVN_ENDLABELEDIT)
{
    int iIndex;
    char text[255]="";

    iIndex=SendMessage(hList,LVM_GETNEXTITEM,
        -1,LVNI_FOCUSED); // get selected item
 
    LvItem.iSubItem=0;     // we get the item only (change for sub)
    LvItem.pszText=text;   // text type
    GetWindowText(hEdit, text, sizeof(text)); // get the text into a buffer

    SendMessage(hList,LVM_SETITEMTEXT,
       (WPARAM)iIndex,(LPARAM)&LvItem); // put new text
}

ListView Colors

You won't believe how much time I took to dig this up to actually work! I mean, 99.99% examples were MFC only!

So here we go, finally, pure WinAPI code for listview colors.

Note: This code is found deeply inside MSDN!

First we handle the WM_NOTIFY message to use a NM_CUSTOMDRAW notification. We don't need to use any owner drawn listview, and since this is custom drawn, we can paint the items/subitems at our will.

if(((LPNMHDR)lParam)->code == NM_CUSTOMDRAW)
{
   SetWindowLong(hWnd, DWL_MSGRESULT, 
        (LONG)ProcessCustomDraw(lParam));
   return TRUE;
}

We will set a new style which will be a result of our painting function.

LRESULT ProcessCustomDraw (LPARAM lParam)
{
    LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;

    switch(lplvcd->nmcd.dwDrawStage) 
    {
        case CDDS_PREPAINT : //Before the paint cycle begins
            //request notifications for individual listview items
            return CDRF_NOTIFYITEMDRAW;
            
        case CDDS_ITEMPREPAINT: //Before an item is drawn
            if (((int)lplvcd->nmcd.dwItemSpec%2)==0)
            {
                //customize item appearance
                lplvcd->clrText   = RGB(255,0,0);
                lplvcd->clrTextBk = RGB(200,200,200);
                return CDRF_NEWFONT;
            }
            else{
                lplvcd->clrText   = RGB(0,0,255);
                lplvcd->clrTextBk = RGB(255,255,255);
            
                return CDRF_NEWFONT;
            }
            break;

        //Before a subitem is drawn
        case CDDS_SUBITEM | CDDS_ITEMPREPAINT: 
            if (iSelect == (int)lplvcd->nmcd.dwItemSpec)
            {
                if (0 == lplvcd->iSubItem)
                {
                    //customize subitem appearance for column 0
                    lplvcd->clrText   = RGB(255,0,0);
                    lplvcd->clrTextBk = RGB(255,255,255);

                    //To set a custom font:
                    //SelectObject(lplvcd->nmcd.hdc, 
                    //    <your custom HFONT>);

                    return CDRF_NEWFONT;
                }
                else if (1 == lplvcd->iSubItem)
                {
                    //customize subitem appearance for columns 1..n
                    //Note: setting for column i 
                    //carries over to columnn i+1 unless
                    //      it is explicitly reset
                    lplvcd->clrTextBk = RGB(255,0,0);
                    lplvcd->clrTextBk = RGB(255,255,255);

                    return CDRF_NEWFONT;
                }
            }
    }
    return CDRF_DODEFAULT;
}

There you code, change/implement code at your will. This is a very good working skeleton of a custom draw, painting items! :-)

---Sub item Colors---

Sub item colors are really cool when you don't wanna have same color for a row. The basic idea is to send a CDRF_NOTIFYSUBITEMDRAW notification to our result. We will send this message first thing when our item is going to be repainted!

switch(lplvcd->nmcd.dwDrawStage) 
{
   case CDDS_PREPAINT : //Before the paint cycle begins
   {
      //request notifications for individual listview items
      return CDRF_NOTIFYITEMDRAW;
   }
   break;
            
   case CDDS_ITEMPREPAINT: //Before an item is drawn
   {
     // Notify we want SubItems to be repainted
     return CDRF_NOTIFYSUBITEMDRAW; 
   }
   break;
}

The only thing left is to trap this notification and to start drawing the sub items with any color/back-color we want, in a single item.

switch(lplvcd->nmcd.dwDrawStage) 
    {
        case CDDS_PREPAINT : //Before the paint cycle begins
            //request notifications for individual listview items
            return CDRF_NOTIFYITEMDRAW;
            
        case CDDS_ITEMPREPAINT: //Before an item is drawn
            {
                return CDRF_NOTIFYSUBITEMDRAW;
            }
            break;

        //Before a subitem is drawn
        case CDDS_SUBITEM | CDDS_ITEMPREPAINT: 
            {
                switch(lplvcd->iSubItem)
                {
                    case 0:
                    {
                      lplvcd->clrText   = RGB(255,255,255);
                      lplvcd->clrTextBk = RGB(240,55,23);
                      return CDRF_NEWFONT;
                    }
                    break;
                    
                    case 1:
                    {
                      lplvcd->clrText   = RGB(255,255,0);
                      lplvcd->clrTextBk = RGB(0,0,0);
                      return CDRF_NEWFONT;
                    }
                    break;  

                    case 2:
                    {
                      lplvcd->clrText   = RGB(20,26,158);
                      lplvcd->clrTextBk = RGB(200,200,10);
                      return CDRF_NEWFONT;
                    }
                    break;

                    case 3:
                    {
                      lplvcd->clrText   = RGB(12,15,46);
                      lplvcd->clrTextBk = RGB(200,200,200);
                      return CDRF_NEWFONT;
                    }
                    break;

                    case 4:
                    {
                      lplvcd->clrText   = RGB(120,0,128);
                      lplvcd->clrTextBk = RGB(20,200,200);
                      return CDRF_NEWFONT;
                    }
                    break;

                    case 5:
                    {
                      lplvcd->clrText   = RGB(255,255,255);
                      lplvcd->clrTextBk = RGB(0,0,150);
                      return CDRF_NEWFONT;
                    }
                    break;

                }
 
            }
    }
    return CDRF_DODEFAULT;

ListView BackGround Image

Isn't it cool to have a picture (JPG, BMP, GIF...) on your ListView color? It makes it a bit more pro look. I guess it is more about personal taste. Anyhow, the ListView will use the OLE object to connect to the image and will draw it over the control.

First, the ListView will use a struct to store information regarding the image and its properties. The LVBKIMAGE struct:

typedef struct tagLVBKIMAGE {
    ULONG ulFlags;   // the way the pic behaves (tile, none, normal..)
    HBITMAP hbm;     // reserved for future!! (HBITMAP)
    LPTSTR pszImage; // path/url to our bitmap if used with LVBKIF_SOURCE_URL
    UINT cchImageMax;//Size of the buffer at the address in pszImage
    int xOffsetPercent; // x pos on listview
    int yOffsetPercent; // y pos on listview
} LVBKIMAGE, *LPLVBKIMAGE;

First thing to do is to initialize the OLE object! Microsoft suggests 2 ways for that: CoInitialize(NULL); or OleInitialize(NULL). The code itself is pretty much self explained!

case WM_INITDIALOG:
{
   LVBKIMAGE plvbki={0};   // set all members to 0 (or use memset)
   char url[]="C:\\a.jpg"; // path to our image 
   //memset(&plvbki,0,sizeof(plvbki));
   plvbki.ulFlags=LVBKIF_SOURCE_URL; // image from a path
   plvbki.pszImage=url; // the path
   plvbki.xOffsetPercent=40; // position
   plvbki.yOffsetPercent=15; // on the list view
   OleInitialize(NULL);  // intialize OLE COM Object
   SendMessage(hList,LVM_SETTEXTBKCOLOR, 
      0,(LPARAM)CLR_NONE); // see below
   SendMessage(hList,LVM_SETBKIMAGE,0,
      (LPARAM)(LPLVBKIMAGE)&plvbki); // do it
}
break;

You can see that I have used CLR_NONE. Now, if your list view is not using custom control (Listview colors, see section above), then this line will set the background color of each row to transparent!

The only thing left to do is to close the OLE COM object. This is what Microsoft suggested anyway, doing it by: CoUninitialize(); or OleUninitialize();. When your application is closed, funny, it causes a crash here. I'll check it later :-)

---Adding Item's Renaming Cancel Event---

Heya, ever wanted that effect when you edit a folder's name and you typed the wrong name, and wanna start over and go back to the original folder's name by ESC key? (you probably know it..)

We want to add the same effect to our listview's items too! For this, we need to learn about Windows messages and process them. Usually think about an EverLasting look that monitors the window you are using, and listens for every message that you process:

Mouse, KeyBoard...etc., you should already be familiar with them. This is what we need to to:

  1. Save the original text of item
  2. Monitor for a key press
  3. If ESC key pressed, re-save old text else

we get the new text and show it on the new item's caption.

Let's start by creating our *Global* vars (so we can access then from any function/message).

//*Global Variables*
bool escKey=0;        // check whatever we pressed ESC
char tempstr[100]=""; // holds original text
TCHAR tchar;          // holds the char we pressed (WM_CHAR message)
MSG msg;              // Message struct to be filled

And now for the re-coding & adding some stuff:

We first begin with adding the below code to the WM_INITDIALOG:

  ShowWindow(hWnd,SW_NORMAL); 
  UpdateWindow(hWnd); 

  while(TRUE)
  {
    if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    {   
      // We must Process this! this is our ticket out of the loop & process
      if(msg.message==WM_QUIT)
      {
         break; // Kill while look
      }
      TranslateMessage(&msg); // Translate the Message (VK->WM_CHAR..etc)
      DispatchMessage(&msg);  // dispatches a message to a window procedure
    }                   
  }

Notice the WM_QUIT. To break the while loop, we must process this when a user hits the [X]. The while will be broken and we will go to the WM_CLOSE message where we must add 1 line:

        case WM_CLOSE:
        {
          PostQuitMessage(0);
          EndDialog(hWnd,0); // kill dialog
        }
        break;

Now for the main part, you already know the messages that Windows sends to the ListView in order to edit an item's caption right?

  • LVN_BEGINLABELEDIT
  • LVN_ENDLABELEDIT

We already have a code for them, but we need to add a slight addition:

   if(((LPNMHDR)lParam)->code == LVN_BEGINLABELEDIT)
   {
     hEdit=ListView_GetEditControl(hList);
     GetWindowText(hEdit, tempstr, sizeof(tempstr)); // NEW
   }

Here we save the text before we actually edit it! That's great and the last part:

if(((LPNMHDR)lParam)->code == LVN_ENDLABELEDIT)
{
   int iIndex;
   char text[255]="";

   tchar = (TCHAR)msg.wParam;// NEW
   if(tchar == 0x1b) // NEW
      escKey=1; // NEW

    iIndex=SendMessage(hList,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
    if(iIndex==-1)
       break;
    LvItem.iSubItem=0; // main item only                   
    
    if(escKey==0) // NEW
    {
       LvItem.pszText=text;
       GetWindowText(hEdit, text, sizeof(text));
       SendMessage(hList,LVM_SETITEMTEXT,
           (WPARAM)iIndex,(LPARAM)&LvItem);
    }
    else{ // All Below is NEW
        LvItem.pszText=tempstr;  // put old text on item's caption
        SendMessage(hList,LVM_SETITEMTEXT,
           (WPARAM)iIndex,(LPARAM)&LvItem);
        escKey=0;
    }
}
break;

We first begin by checking tchar's content...why?

Ok, well, you probably know that ENTER /ESC and some more are keys that will finish up the editing of the text! So we can't check for those keys in the while() loop, because it will be skipped by the LVN_ENDLABELEDIT message. So first thing we check is, if ESC was hit.

Using a boolean variable, we can determine whenever a cancel event has occurred, or we can actually process the new text.

Selecting (Highlighting) items/All Items

It is not really hard, we use this piece of code:

Select Item:
ListView_SetItemState(hList, -1, 0, LVIS_SELECTED); // deselect all items
SendMessage(hList,LVM_ENSUREVISIBLE ,
      (WPARAM)item,FALSE); // if item is far, scroll to it
ListView_SetItemState(hList,item,
      LVIS_SELECTED ,LVIS_SELECTED); // select item
ListView_SetItemState(hList,item,
      LVIS_FOCUSED ,LVIS_FOCUSED); // optional

Select All Item:
ListView_SetItemState(hList, -1, 0, 
      LVIS_SELECTED); // deselect all items
ListView_SetItemState(hList,-1,
      LVIS_SELECTED ,LVIS_SELECTED);

The -1 means ALL items. If you want an individual item, just write its index number.

Bugs and Help

It could be that you are using Win2000 for compiling this source code and you may happen to get into a few problems of linking or compiling..

Problem

The thread exits with code 0x0 (exit 0) // something like this :).

When you compile your exe and try to run it, it wont run. Nothing is loading..??? This is because we didn't link with the comctl32.lib. Add it to your link settings: Project -> Settings -> Link Tab -> general -> add comctl32.lib into "object/libary modules" -> click OK. Rebuild project.. and try to run. It's suppose to work. Another way of solving this is to add this code to your WinMain:

    // add this code if win2000/nt/xp doesn't
    // load ur winmain (experimental only)
    // also add comctl32.lib

    INITCOMMONCONTROLSEX InitCtrls;
        InitCtrls.dwICC = ICC_LISTVIEW_CLASSES;
        InitCtrls.dwSize = sizeof(INITCOMMONCONTROLSEX);
        BOOL bRet = InitCommonControlsEx(&InitCtrls);

It could work, I haven't tried it..

Yet another approach is directly via the: InitCommonControls(); function!! Write it in the case WM_INITDIALOG:

---F.A.Q---

Q. Why sometimes I get wrong result from the LVM_GETNEXTITEM when I select items out of the range?

A. This is because we have to set a flag called LVNI_SELECTED:

SendMessage(hList,LVM_GETNEXTITEM,-1,LVNI_FOCUSED|LVNI_SELECTED);

Q. How do I attach a popup menu when clicking on an item (right button...etc.)?

A. Really easy. First make the menu in the resource editor, and use this code:

HMENU hMenu = LoadMenu (NULL, MAKEINTRESOURCE (IDR_MENU));
HMENU hPopupMenu = GetSubMenu (hMenu, 0);
POINT pt;
SetMenuDefaultItem (hPopupMenu, -1, TRUE);
GetCursorPos (&pt);
SetForegroundWindow (hWnd);
TrackPopupMenu (hPopupMenu, 
  TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL);
SetForegroundWindow (hWnd);
DestroyMenu (hPopupMenu);
DestroyMenu (hMenu);

Note: Make sure you pressed on an item, use a if(selected!=-1) or alike code. That way we prevent from showing the popup menu anytime/anywhere.

Q. How do I color the: entire text color, list view back color, text back color?

A. It is not really hard. We can do it like this:

int count=SendMessage(GetDlgItem(hWnd,
        IDC_LIST),LVM_GETITEMCOUNT ,0,0);
SendMessage(GetDlgItem(hWnd,IDC_LIST),
        LVM_SETTEXTCOLOR,0,(LPARAM)SetColor());
SendMessage(GetDlgItem(hWnd,IDC_LIST),
        LVM_SETTEXTBKCOLOR,0,(LPARAM)SetColor());
SendMessage(GetDlgItem(hWnd,IDC_LIST),
        LVM_SETBKCOLOR,0,(LPARAM)SetColor());
SendMessage(GetDlgItem(hWnd,IDC_LIST),
        LVM_REDRAWITEMS ,0,count);
UpdateWindow(hWnd);
..
..
..
LONG SetColor()
{
    static CHOOSECOLOR cc ;
    static COLORREF    crCustColors[16] ;

    cc.lStructSize    = sizeof (CHOOSECOLOR) ;
    cc.hwndOwner      = NULL ;
    cc.hInstance      = NULL ;
    cc.rgbResult      = RGB (0x80, 0x80, 0x80) ;
    cc.lpCustColors   = crCustColors ;
    cc.Flags          = CC_RGBINIT | CC_FULLOPEN ;
    cc.lCustData      = 0 ;
    cc.lpfnHook       = NULL ;
    cc.lpTemplateName = NULL ;

    ChooseColor (&cc);
    return cc.rgbResult;
}

Note: However, this code has a really tiny bug. If the user presses the cancel/[X] from the color dialog, the default color of it will be returned, and colors your default list box color. However, it's not really hard to fix.

Q. How can I edit an item's caption using a button/menu...etc., just like renaming a folder with right click on mouse option?

A. Send the message: LVM_EDITLABEL to the list view. Example: SendMessage(hList,LVM_EDITLABEL ,(WPARAM)index,(LPARAM)0);.

We come to the end of this article. I hope everyone enjoyed this as much as it took me time to find and add a proper code ;-).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here