change malloc() to calloc() so arrays are blanked
add a frun boolean, so realloc can be used properly
add skinning
add button functions
add smtp support
add google image support

-----------------------------------------------------------------------------------------------------------------
from msdn:
 MSDN Home >  MSDN Library >  Win32 and COM Development >  User Interface > 
Windows Shell >  Windows Controls >  Individual Control Information >  List-View
Controls >
Using List-View Controls

This section contains code examples that demonstrate how to create and use
list-view controls in your applications.

    * Creating List-View Controls
    * Adding List-View Image Lists
    * Adding List-View Columns
    * Adding List-View Items and Subitems
    * Using List-View Working Areas
    * Using Virtual List-View Controls
* Related Topics

Creating List-View Controls

To create a list-view control, you use the CreateWindow or CreateWindowEx
function and specify the WC_LISTVIEW window class. The following code sample
creates a list-view control in report view.

// CreateListView - creates a list-view control in
// report view.
// Returns the handle to the new control if successful,
// or NULL otherwise.
// hWndParent - handle to the control's parent window.

HWND CreateListView (HWND hwndParent)
{
HWND hWndListView;
RECT rcl;
INITCOMMONCONTROLSEX icex;

// Ensure that the common control DLL is loaded.

icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC  = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);

// Create the list-view window in report view with label
// editing enabled.

GetClientRect (hWndParent, &rcl);
hWndListView = CreateWindow (WC_LISTVIEW, "",
        WS_CHILD | LVS_REPORT | LVS_EDITLABELS,
        0, 0, rcl.right - rcl.left, rcl.bottom -
        rcl.top,
        hwndParent, (HMENU) ID_LISTVIEW, hInst, NULL);
if (hWndListView == NULL)
return NULL;

A list-view control can also be created as part of a dialog box template.
Specify WC_LISTVIEW as the class name. Note that if you use a list-view control
as part of a dialog box template, you must call InitCommonControls or
InitCommonControlsEx before the dialog box template is created, in order to
ensure that the list-view window class is properly registered. Otherwise, the
dialog box template does not create the list-view control successfully.

The following illustration shows the four views that a list-view control can
have.

Windows with list-view controls.

Typically, list-view applications enable the user to change from one view to
another. The following code sample changes the list-view's window style, which
in turn changes the view.

// SetView - sets a list-view's window style to change
// the view.
// hWndListView - handle to the list-view control.
// dwView - value specifying the new view style.

VOID SetView(HWND hWndListView, DWORD dwView)
{
// Retrieve the current window style.
DWORD dwStyle = GetWindowLong(hWndListView, GWL_STYLE);
// Only set the window style if the view bits have
// changed.
if ((dwStyle & LVS_TYPEMASK) != dwView)
    SetWindowLong(hWndListView, GWL_STYLE,
       (dwStyle & ~LVS_TYPEMASK) | dwView);
}

Adding List-View Image Lists

When a list-view control is created by default, it does not display item images.
You must assign an image to the list-view control. To do this, use the
LVM_SETIMAGELIST message or the corresponding macro ListView_SetImageList,
specifying whether the image list contains full-sized icons, small icons, or
state images. To retrieve the handle to an image list currently assigned to a
list-view control, use the LVM_GETIMAGELIST message. You can use the
GetSystemMetrics function to determine appropriate dimensions for the full-sized
and small icons.

You need to create only the image lists that the control uses. For example, if
your application does not allow the user to switch to icon view, you do not need
to create and assign a large icon list. If you create both large and small image
lists, they must contain the same images in the same order, because a single
value is used to identify a list-view item's icon in both image lists.

// InitListViewImageLists - creates image lists for a
// list-view control.
// This function only creates image lists. It does not
// insert the items into the control, which is necessary
// for the control to be visible.
// Returns TRUE if successful, or FALSE otherwise.

// hWndListView - handle to the list-view control.

BOOL InitListViewImageLists(HWND hWndListView)
{
    HICON hiconItem;     // icon for list-view items
    HIMAGELIST hLarge;   // image list for icon view
    HIMAGELIST hSmall;   // image list for other views

// Create the full-sized icon image lists.
    hLarge = ImageList_Create(GetSystemMetrics(SM_CXICON),
    GetSystemMetrics(SM_CYICON), ILC_MASK, 1, 1);
    hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
    GetSystemMetrics(SM_CYSMICON), ILC_MASK, 1, 1);

// Add an icon to each image list.
    hiconItem = LoadIcon(g_hinst,
	   MAKEINTRESOURCE(IDI_ITEM));
    ImageList_AddIcon(hLarge, hiconItem);
    ImageList_AddIcon(hSmall, hiconItem);
    DestroyIcon(hiconItem);

/******************************************************
Usually you have multiple icons; therefore, the previous
four lines of code can be inside a loop. The following
code shows such a loop. The icons are defined in the
application's header file as resources, which are
numbered consecutively starting with IDS_FIRSTICON. The
number of icons is defined in the header file as
C_ICONS.

for(index = 0; index < C_ICONS; index++)
    {
    hIconItem = LoadIcon (hInst, MAKEINTRESOURCE
	   (IDS_FIRSTICON + index));
    ImageList_AddIcon(hSmall, hIconItem);
    ImageList_AddIcon(hLarge, hIconItem);
    Destroy(hIconItem);
    }
   *******************************************************/
// Assign the image lists to the list-view control.
    ListView_SetImageList(hwndLV, hLarge, LVSIL_NORMAL);
    ListView_SetImageList(hwndLV, hSmall, LVSIL_SMALL);
    return TRUE;
}

Adding List-View Columns

Columns are used when a list-view control is in report view to display the items
and subitems. The following example adds columns to a list-view control. The
column headings are defined in the application's header file as string
resources, which are numbered consecutively starting with IDS_FIRSTCOLUMN. The
number of columns is defined in the header file as C_COLUMNS.

// InitListViewColumns - adds columns to a list-view
// control.
// Returns TRUE if successful, or FALSE otherwise.
// hWndListView - handle to the list-view control.
BOOL InitListViewColumns(HWND hWndListView)
{
    char szText[256];     // temporary buffer
    LVCOLUMN lvc;
    int iCol;

// Initialize the LVCOLUMN structure.
// The mask specifies that the format, width, text, and
// subitem members of the structure are valid.
    lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT |
	  LVCF_SUBITEM;

// Add the columns.
    for (iCol = 0; iCol < C_COLUMNS; iCol++)

	{
        lvc.iSubItem = iCol;
        lvc.pszText = szText;
        lvc.cx = 100;     // width of column in pixels

        if ( iCol < 2 )
          {
          lvc.fmt = LVCFMT_LEFT;  // left-aligned column
          }
        else
          {
          lvc.fmt = LVCFMT_RIGHT; // right-aligned column
          }
        LoadString(hInst, IDS_FIRSTCOLUMN + iCol,
                szText, sizeof(szText)/sizeof(szText[0]));
        if (ListView_InsertColumn(hWndListView, iCol,
		        &lvc) == -1)
            return FALSE;
    }
    return TRUE;
}

Adding List-View Items and Subitems

To add an item to a list-view control, an application must first define an
LVITEM structure and then send an LVM_INSERTITEM message, specifying the address
of the LVITEM structure.

If an application uses report view, subitem text must be entered. The following
code samples fill an LVITEM structure and add the list-view items by using the
LVM_INSERTITEM message or the corresponding macro ListView_InsertItem. Because
the application saves its own text, it specifies the LPSTR_TEXTCALLBACK value
for the pszText member of the LVITEM structure. Specifying the
LPSTR_TEXTCALLBACK value causes the control to send an LVN_GETDISPINFO
notification message to its owner window whenever it needs to display an item.

// This code snippet adds three items, each with three
// subitems, to a list-view control.
// hWndListView - handle to the list-view control.
// The following application-specific structure,
// PETINFO, is used in the snippet.

typedef struct tagPETINFO
{
char szKind[10];
char szBreed[50];
char szPrice[20];
}PETINFO;

// A PETINFO variable is declared and initialized as
// follows:

PETINFO rgPetInfo[ ] =
{
{"Dog", "Poodle", "$300.00"},
{"Cat", "Siamese", "$100.00"},
{"Fish", "Angel Fish", "$10.00"},
};

// Some code to create the list-view control.
// Initialize LVITEM members that are common to all
// items.
lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
lvI.state = 0;
lvI.stateMask = 0;

// Initialize LVITEM members that are different for each item.
for (index = 0; index < 3; index++)
{
   	lvI.iItem = index;
	lvI.iImage = index;
	lvI.iSubItem = 0;
	lvI.lParam = (LPARAM) &rgPetInfo[index];
	lvI.pszText = LPSTR_TEXTCALLBACK; // sends an
	                                  // LVN_GETDISP
                                      // message.
if(ListView_InsertItem(hWndListView, &lvI) == -1)
return NULL;
}

// Because the application specifies LPSTR_TEXTCALLBACK, it
// sends LVN_GETDISPINFO notification messages. The
// application must process these notification messages and
// supply the text for the subitems.
// The text is stored in a previously listed
// application-defined structure called PETINFO.

case WM_NOTIFY:
  switch (((LPNMHDR) lParam)->code)
  {
  case LVN_GETDISPINFO:
    switch (((LPNMLVDISPINFO)lParam)->item.iSubItem)
    {
    case 0:
      plvdi->item.pszText =
	    rgPetInfo[plvdi->item.iItem].szKind;
      break;

    case 1:
      plvdi->item.pszText =
	    rgPetInfo[plvdi->item.iItem].szBreed;
      break;

    case 2:
      plvdi->item.pszText =
	    rgPetInfo[plvdi->item.iItem].szPrice;
      break;

    default:
      break;
    }
    return 0;
  }

// NOTE: in addition to setting pszText to point to
// the item text, you could copy the item text into pszText
// using StringCchCopy. For example:
// StringCchCopy(rgPetInfo[plvdi->item.iItem].szKind,
//                          sizeof(rgPetInfo[plvdi->item.iItem].szKind),
//                          plvdi->item.pszText);

Using List-View Working Areas

When a working area is created, items that lie within the working area become
members of it. Similarly, if an item is moved into a working area, the item
becomes a member of the working area to which it was moved. If an item does not
lie within any working area, it automatically becomes a member of the first
(index 0) working area. If you want to create an item and have it placed within
a specific working area, you will need to create the item and then move it into
the desired working area using an LVM_SETITEMPOSITION or LVM_SETITEMPOSITION32
message.

The following illustration is an example of a list-view control that contains
four working areas, each in a different quadrant of the client area.

List-view control that contains four working areas.

To determine which working area an item is a member of, retrieve the working
areas, retrieve the position of the item, and compare its position to the
working areas. The following function returns the index of the working area to
which the item belongs. If the function fails, it returns -1. If the function
succeeds, but the item is not inside any of the working areas, the function
returns 0, because all items that are not inside a working area automatically
become a member of working area zero.

int GetItemWorkingArea(HWND hWndListView, int iItem)
{
UINT     uWorkAreas = 0;
int      nReturn = -1;
LPRECT   pRects;
POINT    pt;

if(!ListView_GetItemPosition(hWndListView, iItem, &pt))
   return nReturn;

ListView_GetNumberOfWorkAreas(hWndListView, &uWorkAreas);

if(uWorkAreas)
   {
   pRects = (LPRECT)GlobalAlloc(GPTR, sizeof(RECT) *
             uWorkAreas);
   if(pRects)
      {
      UINT  i;

      nReturn = 0;

      ListView_GetWorkAreas(hWndListView, uWorkAreas, pRects);
      for(i = 0; i < uWorkAreas; i++)
         {
         if(PtInRect((pRects + i), pt))
            {
            nReturn = i;
            break;
            }
         }
       GlobalFree((HGLOBAL)pRects);
      }
   }
return nReturn;
}

Multiple working areas can be used for creating different areas within one view.
You can create areas in a single view that have different meanings. For example,
a view of a file system might have an area for read/write files and another area
for read-only files. The user can categorize items by placing them in different
working areas. If a file were moved into the read-only area, it would
automatically become read-only.

Multiple working areas can intersect, but any items that lie within the
intersection become members of the area with the lower index; therefore, it is
best to avoid this situation. When sorting multiple work areas, the items are
sorted compared to the other items in the same working area.

The following code examples show how to create working areas. The first example
creates one working area with a 25-pixel empty space around the left, top, and
right sides of the items.

{
#define  EMPTY_SPACE   25

RECT  rcClient;

GetClientRect(hwndListView, &rcClient);

rcClient.left += EMPTY_SPACE;
rcClient.top += EMPTY_SPACE;
rcClient.right -= (EMPTY_SPACE * 2);

SendMessage(hWndListView, LVM_SETWORKAREAS, 1,
(LPARAM)&rcClient);
}

This example demonstrates how to create two working areas in a control that take
up about half of the client area and have a 25-pixel border around each working
area.

{
#define  EMPTY_SPACE   25

RECT  rcClient;
RECT  rcWork[2];

GetClientRect(hwndListView, &rcClient);

rcWork[0].left = rcClient.left + EMPTY_SPACE;
rcWork[0].top = rcClient.top + EMPTY_SPACE;
rcWork[0].right = (rcClient.right/2) - EMPTY_SPACE;
rcWork[0].bottom = rcClient.bottom;

rcWork[1].left = (rcClient.right/2) + EMPTY_SPACE;
rcWork[1].top = rcClient.top + EMPTY_SPACE;
rcWork[1].right = rcClient.right - EMPTY_SPACE;
rcWork[1].bottom = rcClient.bottom;

SendMessage(hWndListView, LVM_SETWORKAREAS, 2, (LPARAM)rcWork);
}

Using Virtual List-View Controls

This section includes sample code used to implement a virtual list-view control.
The code contains application-defined functions and structures that implement
aspects of a virtual list-view control and perform cache management.
Name Description
Sample Function: OnNotify Handles virtual list-view notification messages.
Sample Function: PrepCache Performs basic cache management.
Sample Function: RetrieveItem Retrieves item information from the cache.
Sample Structure: RndItem Holds item information for use with a cache.

NoteThis sample code assumes that the cache used is a dynamically allocated
array of the application-defined structure Sample Structure: RndItem. The
list-view control in the example is assumed to have three columns—one column for
the item label and two columns for subitems.
Sample Function: OnNotify

This application-defined function handles notification messages commonly sent
from a virtual list-view control.

LRESULT OnNotify( HWND hwnd, NMHDR* pnmhdr )
{
    LRESULT lrt = FALSE;

    switch (pnmhdr->code){
        case LVN_GETDISPINFO:
            RndItem rndItem;
            NMLVDISPINFO* plvdi = (LPNMLVDISPINFO) pnmhdr;
            if (-1 == plvdi->item.iItem){
                OutputDebugString("LVOWNER: Request for -1
                                 item?\n");
                DebugBreak();
            }
            // Retrieve information for item at index iItem.
            RetrieveItem( &rndItem, plvdi->item.iItem );

            if(plvdi->item.mask & LVIF_STATE){
                // Fill in the state information.
                plvdi->item.state |= rndItem.state;
            }
            if(plvdi->item.mask & LVIF_IMAGE){
                // Fill in the image information.
                plvdi->item.iImage = rndItem.iIcon;
            }
            if(plvdi->item.mask & LVIF_TEXT){
                // Fill in the text information.
                switch (plvdi->item.iSubItem){
                    case 0:
                        // Copy the main item text.
                        hr = StringCchCopy
                        ( plvdi->item.pszText, MAX_COUNT,
                        rndItem.Title );
                        if(FAILED(hr))
                          {
                          // TODO: Write an error handler. MAX_COUNT
                          // is a user-defined value. You must not enter
                          // more characters than specified by MAX_COUNT or
                          // the text will be truncated.
                          }
                        break;

                    case 1:
                        // Copy subitem 1 text.
                        hr = StringCchCopy
                        ( plvdi->item.pszText, MAX_COUNT,
                        rndItem.SubText1 );
                        if(FAILED(hr))
                          {
                          // TODO: Write an error handler. MAX_COUNT
                          // is a user-defined value. You must not enter
                          // more characters than specified by MAX_COUNT or
                          // the text will be truncated..
                          }
                         break;

                    case 2:
                        // Copy subitem 2 text.
                        hr = StringCchCopy
                        ( plvdi->item.pszText, MAX_COUNT,
                        rndItem.SubText2 );
                        if(FAILED(hr))
                          {
                          // TODO: Write an error handler. MAX_COUNT
                          // is a user-defined value. You must not enter
                          // more characters than specified by MAX_COUNT or
                          // the text will be truncated..
                          }
                         break;

                    default:
                        break;
                }
            }
            lrt = FALSE;
            break;

    case LVN_ODCACHEHINT:
        NMLVCACHEHINT* pcachehint = (NMLVCACHEHINT*) pnmhdr;

        // Load the cache with the recommended range.
        PrepCache( pcachehint->iFrom, pcachehint->iTo );
        break;

    case LVN_ODFINDITEM:
        LPNMLVFINDITEM pnmfi = (LPNMLVFINDITEM)pnmhdr;

        // Call a user-defined function to find the index
        // according to LVFINDINFO, which is embedded in the
        // LPNMLVFINDITEM structure. Set return value to -1
        // if no hits are found.
        break;

    default:
        break;

    return( lrt );
}

Sample Function: PrepCache

This function accepts the range values for the cache suggested by a virtual
list-view control. It performs a verification to determine that the requested
range is not already cached, and then it allocates the required global memory
and fills the cache if necessary.

void PrepCache( int iFrom, int iTo )
{
/*  Global Variables
*  g_priCache[ ] is the main cache.
*  g_iCache is the index of the first item in the main cache.
*  c_cCache is the count of items in the main cache.
*
*  g_priEndCache[ ] is the cache of items at the end of the
*  list.
*  g_iEndCache is the index of the first item in the end
*  cache.
*  g_cEndCache is the count of items in the end cache.
*/
    // Local Variables
    int i;
    BOOL fOLFrom = FALSE;
    BOOL fOLTo = FALSE;

    // Check to see if this is the end cache.
    if ((iTo == g_cItems - 1) && ((iTo - iFrom) < 30))
	                                      // 30 entries wide
	{
    // Check to see if this is a portion of the current end
    // cache.
        if ((g_cCache) &&
            (iFrom >= g_iEndCache) &&
            (iFrom < g_iEndCache+g_cEndCache))
            return;
            // If it is a part of current end cache, no
            // loading is necessary.

        // This is a new end cache; free the old memory.
        if ( g_priEndCache )
            GlobalFree( g_priEndCache );

        // Set the index and count values for the new end
        // cache; then retrieve the memory.
        g_iEndCache = iFrom;
        g_cEndCache = (iTo - iFrom + 1);
        g_priEndCache = (PRndItem)GlobalAlloc( GPTR,
		                 sizeof( RndItem ) * g_cEndCache );

        if (! g_priEndCache);
        // Out of memory. Perform error handling operations.

        // Loop to fill the cache with the recommended items.
        for (i=0; i<g_cEndCache; i++);
        // Call function that accesses item information and
        // fills a cache element here.
    }

    else{     // It is not a member of the current end cache.
              // Try the primary cache instead.

        // Check to see if iFrom is within the primary cache.
        if ((g_cCache) &&
            (iFrom >= g_iCache) &&
            (iFrom < g_iCache+g_cCache))
            fOLFrom = TRUE;

        // Check to see if iTo is within the primary cache.
        if ((g_cCache) &&
            (iTo >= g_iCache) &&
            (iTo <= g_iCache+g_cCache))
            fOLTo = TRUE;

        // If both iFrom and iTo are within current cache, no
        // work is needed.

        if (fOLFrom && fOLTo)
            return;

        // Enlarge the cache size rather than make it specific
        // to this hint.
        if (fOLFrom)
            iFrom = g_iCache;
        else if (fOLTo)
            iTo = g_iCache + g_cCache;

        // A new primary cache is needed; free the old one.
        if ( g_priCache )
            GlobalFree( g_priCache );

        // Set the index and count values for the new primary
        // cache, and then retrieve the memory.
        g_iCache = iFrom;
        g_cCache = (iTo - iFrom + 1);
        g_priCache = (PRndItem)GlobalAlloc( GPTR, sizeof
		             ( RndItem ) * g_cCache );

        if (!g_priEndCache);
        // Out of memory. Do error handling.

        // Loop to fill the cache with the recommended items.
        for (i=0; i<g_cEndCache; i++)
        // Call function that accesses item information and
        // fills a cache element here.
    }
}

Sample Function: RetrieveItem

This sample function accepts two parameters—the address of the
application-defined structure Sample Structure: RndItem and an integer value
representing the index of the item in the list. It checks the index value to
discover if the desired item is cached. If it is, the pointer that was passed to
it is set to a location in the cache. If the item is not in the main or end
cache, the item information must be located manually.

void RetrieveItem( PRndItem prndItem, int index )
{
/*  Global Variables
*    g_priCache[ ] is the main cache.
*    g_iCache is the index of the first item in the main
*    cache.
*    c_cCache is the count of items in the main cache.
*
*    g_priEndCache[ ] is the cache of items at the end of the
*    list.
*    g_iEndCache is the index of the first item in the end
*    cache.
*    g_cEndCache is the count of items in the end cache.
*/

    // Check to see if the item is in the main cache.
    if ((index >= g_iCache) && (index < g_iCache + g_cCache))
        *prndItem = g_priCache[index-g_iCache];

    // If it is not in the main cache, check to see if the item
    // is in the end area cache.
    else if ((index >= g_iEndCache)
              && (index < g_iEndCache + g_cEndCache))
        *prndItem = g_priEndCache[index-g_iEndCache];
    else{
        // The item is not in either cache;
        // you should retrieve the item information manually.
    }
}

Sample Structure: RndItem

This sample user-defined structure holds information placed in the cache. The
structure has the following form.

typedef struct _RndItem
{
    int   iIcon;            // bitmap assigned to this item
    TCHAR Title[SIZE];      // SIZE is a user-defined macro
                            // value
    UINT  state;            // item state value
    TCHAR SubText1[SIZE];   // text for the label of the first
                            // subitem
    TCHAR SubText2[SIZE];   // text for the label of the second
                            // item
} RndItem, *PRndItem;

Related Topics

For complete samples that demonstrate how to create and use list-view controls,
see the following:

* ListView1 sample

For a list of the window messages processed by a list-view control, see:

* Default List-View Message Processing
