Using text callbacks
Suppose your data is stored in your app in structures "ItemStruct" declared:
typedef struct {
int nItemNo;
CString strName;
} ItemStruct;
and each structure is stored in an array, list or map. The easiest way to let the list know about each item is to store a pointer to each item in the lParam field of the LV_ITEM structure you pass to the CListCtrl::InsertItem function.
Firstly, when you add a new item to the list you should set the LVIF_PARAM bit in the mask field of your LV_ITEM structure that you are using. This lets the list know that the lParam field of your LV_ITEM contains data.
You may want to create a helper function to add a new item to the list like:
BOOL CMyListCtrl::AddItem(ItemStruct* pItem, int nPos /* = -1 *
)
{
int nNumItems = GetItemCount();
int nInsertPos = (nPos >= 0 && nPos <= nNumItems)? nPos : nNumItems;
LV_ITEM Item;
Item.lParam = (LPARAM) pItem; // Store the pointer to the data
Item.pszText = LPSTR_TEXTCALLBACK; // using callbacks to get the text
Item.mask = LVIF_TEXT | LVIF_PARAM; // lParam and pszText fields active
Item.iItem = nInsertPos; // position to insert new item
Item.iSubItem = 0;
if (InsertItem(&Item) < 0)
return FALSE;
else
return TRUE;
}
The LPSTR_TEXTCALLBACK value for the pszText field tells the list that it should use callbacks to get the text to display, instead of storing the text itself. Every time the list needs to display text, it sends a LVN_GETDISPINFO. We don't add text for the subitems, since they will be dealt with in the LVN_GETDISPINFO handler as well.
You need to handle the windows notification LVN_GETDISPINFO using the following:
BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CMyListCtrl)
ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetDispInfo)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
and have an accompanying function declared in you class definition:
class CMyListCtrl
{
// .. other declarations
// Generated message map functions
protected:
//{{AFX_MSG(CMyListCtrl)
afx_msg void OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG
};
The handler function should look something like:
void CMyListCtrl::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
if (pDispInfo->item.mask & LVIF_TEXT)
{
ItemStruct* pAppItem = (ItemStruct*) pDispInfo->item.lParam;
CString strField = QueryItemText(pDispInfo->item.iItem,
pDispInfo->item.iSubItem);
LPTSTR pstrBuffer = AddPool(&strField);
pDispInfo->item.pszText = pstrBuffer;
}
*pResult = 0;
}
The NMHDR variable contains the list view display info, which in turn holds a LV_ITEM member specifying the item and subitem it's after, and what sort of info it's after. In our case we are only specifying text, so we only deal with notifications where the pDispInfo->item.mask equals LVIF_TEXT. We get a pointer to our Apps data through the lParam field of LV_ITEM, and from this we get a text representation of our data (I'm using another helper function "QueryItemText" implemented as
// returns a CString representation for the data in row nRow, column nCol
CString CMyListCtrl::QueryItemText(int nRow, int nCol)
{
CString strField;
ItemStruct* pAppItem = (ItemStruct*) GetItemData(nRow);
if (!pAppItem) return strField;
switch (nCol) {
case 0: strField.Format("%d",pAppItem->nItemNo); break;
case 1: strField = pAppItem->strName; break;
default: ASSERT(FALSE);
}
return strField;
}
The main reason I use the "QuesryItemText" function is that since we are now using text callbacks, functions like CListCtrl::GetItemText no longer work. If you use GetItemText (for instance in TitleTips or InPlaceEdit's) then you must replace them with QueryItemText in order for them to work. The two lines:
LPTSTR pstrBuffer = AddPool(&strField); pDispInfo->item.pszText = pstrBuffer;
in the OnGetDispInfo function were taken from Mike Blaszczak's program "ApiBrow" (get it either online or from his book "Proffessional MFC with Visual C++ 5"). It handles the bizarre situtation that the list control requires the buffer you pass back from the OnGetDispInfo to be valid for 2 more LVN_GETDISPINFO notifications. His solution was a simple caching to store sufficient copies so the list control doesn't have a cow. It goes something like this:
LPTSTR CMyListCtrl::AddPool(CString* pstr)
{
LPTSTR pstrRetVal;
int nOldest = m_nNextFree;
m_strCPool[m_nNextFree] = *pstr;
pstrRetVal = m_strCPool[m_nNextFree].LockBuffer();
m_pstrPool[m_nNextFree++] = pstrRetVal;
m_strCPool[nOldest].ReleaseBuffer();
if (m_nNextFree == 3)
m_nNextFree = 0;
return pstrRetVal;
}
You will need to declare the protected attributes
CString m_strCPool[3]; LPTSTR m_pstrPool[3]; int m_nNextFree;
in your class definition.
And that's all there is to it!
One advantage of storing the data in the lParam field is that sorting become very quick. A column sort function as already been provided in in the MFC programmers sourcebook. It is prototyped:
BOOL CMasterListCtrl::SortTextItems(int nCol, BOOL bAscending, int low /*= 0* , int high /*= -1* );
I will ignore the high and low parameters (sorry!) and instead implement a different version: (see the docs on CListCtrl::SortItems for details)
typedef struct {
int nColumn;
BOOL bAscending;
} SORTINFO;
BOOL CMasterListCtrl::SortTextItems(int nCol, BOOL bAscending)
{
CWaitCursor waiter;
SORTINFO si;
si.nColumn = m_nSortColumn;
si.bAscending = m_bSortAscending;
return SortItems(CompareFunc, (LPARAM) &si);
}
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
ItemStruct *pItem1 = (ItemStruct*) lParam1;
ItemStruct *pItem2 = (ItemStruct*) lParam2;
SORTINFO* pSortInfo = (SORTINFO*) lParamSort;
ASSERT(pItem1 && pItem2 && pSortInfo);
int result;
switch (pSortInfo->nColumn) {
case 0: if (pItem1->nItemNo < pItem2->nItemNo)
result = -1;
else if (pItem1->nItemNo == pItem2->nItemNo)
result = 0;
else
result = 1;
break;
case 1: result = pItem1->strName.Compare(pItem2->strName);
break;
default: ASSERT(FALSE);
}
if (!pSortInfo->bAscending) result = -result;
return result;
}

Comments
Launch 2013 a new color schematic Nike Air Max shoes
Posted by Tufffruntee on 04/25/2013 01:48amQualifying series Nike Atmosphere Max HomeTurf metropolis recently completely comes up, this series in the exemplary Tune Max shoes to London, Paris and Milan [url=http://www.nikeskoroutlet.se/nike-air-max-90-c-1/]air max 90 billigt[/url] the three paid glorification to the iconic city of Europe, combined with the characteristics of the three cities, Bearing Max 1 HYP,Current Max 90 HYP,Air Max 1 and shoes such as Music pretension Max 95, combined with the Hyperfuse, as by a long chalk as a order of materials, such as suede, Whether you require functioning or retro-everything. It seems to me that more than Nike [url=http://www.nikeskornatet.se/nike-lunarglide-4-c-25/]nike lunarglide 4 dam[/url] shoes all supernova dissimulate sanctioned signature shoe series, and NSW series, all star color system of the original Air Max Hyperposite exposure. Ray daybreak brown vexed, hyperfuse, swoosh, and are attached at the cessation of the yellow/orange gradient split details, lining is a Galaxy in [url=http://www.airmax90rea.se/nike-air-max-bw-c-5/]nike air max bw[/url] orange prototype garter, acutely beautiful.
ReplyNike Draught Max+instagram, desire you take the color to wear on your feet!
Posted by madytreathy on 04/24/2013 10:42pmCall to mind in 2008, if not earlier, when Nike launched winning of the independent shoe color projects, the watchword "Bound Your Colours", "Nike PhotoiD" layout, [url=http://fossilsdirect.co.uk/glossarey.cfm]nike huarache free[/url] effect has not been as fervent as expected. Have in mind, 2008 Canon IXUS 80 IS Digital prankster arcade but contrariwise 8 million pixels, Nokia, the facile phone market-place is the exclusively governorship, NikeiD was support to color in the photos as a basis in return sneakers custom color, although interesting, but does bother some. Instagram which make this thing hold up to ridicule and fundamental, Nike PHOTOiD homeopathic upgrade customization services, recently [url=http://markwarren.org.uk/property-waet.cfm]air max 90 uk[/url] released a fresh plan. That such iD can you utensil pictures as instagram account shoe color, little while volunteer Nike Air Max shoes and Nike Refresh Max 1, Nike Affectedness Max 90 953 options. Interested in children's shoes, you [url=http://markwarren.org.uk/goodbuy.cfm]nike free run uk[/url] can always go's proper website photoid.Nike.com, in beyond to flick through other people's creative work, or you can try to upload your own instagram photo, build your own Nike Mood Max.
ReplyVirtual ListCtrl and Curent
Posted by Legacy on 07/04/2002 12:00amOriginally posted by: Ashmarti
What is difference between Vitual List Control and presented here method??? I think that is the same.
ReplyIs not it?
Something wrong with function AddItem...
Posted by Legacy on 07/26/2001 12:00amOriginally posted by: Aleksey Ulitsky
Hi all,
Probably in the function AddItem is missing an assignment of the pointer ItemStruct *pItem. It should be saved in the in the item if the list control as it's data. Therefore, I changed last lines like folowing:
...........................................//AddItem
if ( index< 0)
return FALSE;
else{
return SetItemData(index, (DWORD)pItem );
}
}///////////////////////////////// end function....
Sincerely,
ReplyAleksey Ulitsky
Test
Posted by Legacy on 12/01/2000 12:00amOriginally posted by: Bill
This is a test post
ReplyProblem:Optimizing
Posted by Legacy on 12/01/2000 12:00amOriginally posted by: Cristian Eigel
I need a CListCtrl that has a large amount of data (from a
database).The solutions are:
1. Set the style of the list control to owner data (LVS_OWNERDATA).The problem is that the list control calls
OnGetDispInfo every time it needs data (even when the mouse moves over an item).If the cache is big, (put into a map)
searching slows down the application.
2. Set the text to LPSTR_TEXTCALLBACK and in OnGetDispInfo
set pDispInfo->item.mask|=LVIF_DI_SETITEM;This makes the
list to store the data on itself, but only the data it needs.
When it needs more data it calls OnGetDispInfo.The problem
is that i need to set LPSTR_TEXTCALLBACK for all items and
subitems in the list.This take a wile also, and makes the
poor user(one that doesn`t own a good computer) to wait a while.
What i need is a combination of the 2 methods.Lets say that
I set the ItemCount first, and then when the application needs data, the list control to store onto itself, and never ask me again about the same data
P.S. Sorry for my bad english.
ReplySimplifying AddPool
Posted by Legacy on 02/11/2000 12:00amOriginally posted by: Ken Hommel
ReplyAddPool - a better solution?
Posted by Legacy on 08/20/1999 12:00amOriginally posted by: Dara
ReplyBug fix for "AddPool" function
Posted by Legacy on 08/05/1999 12:00amOriginally posted by: Doron Bubbai
ReplyDon't forget to intialize m_nNextFree!
Posted by Legacy on 04/11/1999 12:00amOriginally posted by: Kevin W. Brown
m_nNextFree must be initiailized to 0 or the pool array will be written out of bounds
Reply