Handling Notifications

The application uses notifications extensively to manipulate the behavior and the appearance of the controls. Because toolbars, status bars, tree view controls, and list view controls all expect notifications, I had to ensure that each control could get the notifications it needed. In the main window procedure for my C application, I simply trapped the WM_NOTIFY message and either handled the toolbar notifications directly or passed the notifications to the handlers I wrote.

For the toolbar, I was interested only in the TTN_NEEDTEXT notification, which is sent whenever the system needs to display a ToolTip associated with a toolbar button. In response to this notification, the application must load the appropriate text string into the lpszText member of the LPTOOLTIPTEXT structure:

case WM_NOTIFY:
lpToolTipText = (LPTOOLTIPTEXT)lParam;
if (lpToolTipText->hdr.code == TTN_NEEDTEXT)
{
LoadString (g_Listing.hInst,
lpToolTipText->hdr.idFrom, // string ID == cmd ID
szBuf,
sizeof (szBuf));
lpToolTipText->lpszText = szBuf;
}
§

My tree view control has a very simple notification handler that handles only the TVN_SELCHANGED notification (which is sent to the tree view control whenever the selection changes). In response to this notification, I needed to update the list view control and the status bar to reflect the house listings for the newly selected city, as shown here:

VOID UpdateListView (HWND hwndLV, int iSelected)
{
int count, index;

// Remove the previous items.
LV_RemoveAllItems (hwndLV);

// Loop through the house listings.
for (index = 0, count = 0; count < g_Listing.NumHouses; count++)
{
// If the house is listed for the new city...
if (strcmp (rgHouses[count].szCity, rgCities[iSelected].szCity) == 0)
{
// Add the house to the list view control.
if (! LV_AddItem (hwndLV, index, &rgHouses[count]))
MessageBox (NULL, "LV_AddItem failed!", NULL, MB_OK);
index++;
}
}
}

Handling notifications for the list view control is a bit more complicated. I implemented this control using a callback that receives the text for each item, so the notification handler needs to trap the LVN_GETDISPINFO notification and fill in the pszText member of the LV_ITEM structure with the appropriate text, depending on the column. This notification handler must also process the LVN_COLUMNCLICK notification, which is sent when the user clicks a column header. In response, the application must sort the items in the list view control based on the criterion specified by the header. For example, if the user clicks the Bedrooms column header, the application sorts the list in ascending order by the number of bedrooms in each house.

I provided a simple callback procedure that is called through the ListView_SortItems function. This procedure then sorts the data using simple math (returning the greater of two values) for columns with integer values and using the strcmp function for columns with string values.

LRESULT LV_NotifyHandler (HWND hWnd, UINT uMsg, WPARAM
wParam, 
LPARAM lParam, HINSTANCE hInst)
{
LV_DISPINFO *pLvdi = (LV_DISPINFO *)lParam;
NM_LISTVIEW *pNm = (NM_LISTVIEW *)lParam;
HOUSEINFO *pHouse = (HOUSEINFO *) (pLvdi->item.lParam);
static char szText [TEMP_LEN];

if (wParam != ID_LISTVIEW)
return 0L;

switch(pLvdi->hdr.code)
{
case LVN_GETDISPINFO:
switch (pLvdi->item.iSubItem)
{
case 0: // address
pLvdi->item.pszText = pHouse->szAddress;
break;

case 1: // city
pLvdi->item.pszText = pHouse->szCity;
break;

case 2: // price
sprintf (szText, "$%u", pHouse->iPrice);
pLvdi->item.pszText = szText;
break;

case 3: // number of bedrooms
sprintf (szText, "%u", pHouse->iBeds);
pLvdi->item.pszText = szText;
break;

case 4: // number of bathrooms
sprintf (szText, "%u", pHouse->iBaths);
pLvdi->item.pszText = szText;
break;

default:
break;
}
break;

case LVN_COLUMNCLICK:
// The user clicked a column header; sort by this criterion.
ListView_SortItems (pNm->hdr.hwndFrom,
ListViewCompareProc,
(LPARAM) (pNm->iSubItem));
break;

default:
break;
}
return 0L;
}