Click here to Skip to main content
15,858,479 members
Articles / Desktop Programming / MFC

Using text callbacks in ListView Controls

Rate me:
Please Sign up or sign in to vote.
5.00/5 (23 votes)
26 Nov 1999CPOL3 min read 269.2K   1.3K   72   36
Shows how to use text callbacks in list controls
  • Download files - 16 Kb

    If you store you application data and you wish to save some time and memory, you can have the list ask your application for the strings to display in the list on the fly, instead of having the list store the strings explicitly. This will reduce the amount of memory your final app needs and will make filling and deleting the list contents quicker, at the expense of slightly slower display time. (If you are displaying several thousand items in the list, the time saved in filling and cleaning the list more than makes up for fractionally slower display). Furthermore, list sorting can become lightning fast.

    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;
    }
  • License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


    Written By
    Founder CodeProject
    Canada Canada
    Chris Maunder is the co-founder of CodeProject and ContentLab.com, and has been a prominent figure in the software development community for nearly 30 years. Hailing from Australia, Chris has a background in Mathematics, Astrophysics, Environmental Engineering and Defence Research. His programming endeavours span everything from FORTRAN on Super Computers, C++/MFC on Windows, through to to high-load .NET web applications and Python AI applications on everything from macOS to a Raspberry Pi. Chris is a full-stack developer who is as comfortable with SQL as he is with CSS.

    In the late 1990s, he and his business partner David Cunningham recognized the need for a platform that would facilitate knowledge-sharing among developers, leading to the establishment of CodeProject.com in 1999. Chris's expertise in programming and his passion for fostering a collaborative environment have played a pivotal role in the success of CodeProject.com. Over the years, the website has grown into a vibrant community where programmers worldwide can connect, exchange ideas, and find solutions to coding challenges. Chris is a prolific contributor to the developer community through his articles and tutorials, and his latest passion project, CodeProject.AI.

    In addition to his work with CodeProject.com, Chris co-founded ContentLab and DeveloperMedia, two projects focussed on helping companies make their Software Projects a success. Chris's roles included Product Development, Content Creation, Client Satisfaction and Systems Automation.

    Comments and Discussions

     
    GeneralI can't get this to work in a Windows Explorer architecture project... Pin
    Ben Aldhouse24-Jun-09 11:10
    Ben Aldhouse24-Jun-09 11:10 
    GeneralUpdating the object data Pin
    John Andzelik3-Sep-08 10:02
    John Andzelik3-Sep-08 10:02 
    QuestionHow do Listview controls store data internally? Pin
    slimsoid13-Apr-05 11:10
    slimsoid13-Apr-05 11:10 
    GeneralFlickering if using more than 300 items Pin
    Benj1028-Dec-04 4:28
    Benj1028-Dec-04 4:28 
    I am using this functionality and have found that the item list will flicker after selecting
    to different one in the list. The list control has more than 300 items. Any ideal?

    thanks

    GeneralTime to set &quot;LPSTR_TEXTCALLBACK&quot; Pin
    v_fsr5-Dec-03 9:32
    v_fsr5-Dec-03 9:32 
    GeneralItem Rect Pin
    Brad Bruce20-Oct-03 8:52
    Brad Bruce20-Oct-03 8:52 
    GeneralHELP! PLEASE! Dialog &amp; List Control Data Exchange Pin
    Charlie Curtis20-Aug-03 9:39
    Charlie Curtis20-Aug-03 9:39 
    GeneralHELP!! Pin
    simonzb7-Aug-03 21:02
    simonzb7-Aug-03 21:02 
    GeneralUpdating LV_ITEM doesn't update in list view display. Pin
    Member 3790511-May-03 11:05
    Member 3790511-May-03 11:05 
    GeneralRe: Updating LV_ITEM doesn't update in list view display. Pin
    BhawnaA2-Jul-07 20:53
    BhawnaA2-Jul-07 20:53 
    GeneralSpeed issue when deleting items. Pin
    David Fleming17-Mar-03 21:27
    David Fleming17-Mar-03 21:27 
    GeneralRe: Speed issue when deleting items. Pin
    David Fleming19-Mar-03 13:01
    David Fleming19-Mar-03 13:01 
    GeneralText in a ListView Pin
    Peterlip27-Feb-03 13:22
    Peterlip27-Feb-03 13:22 
    GeneralRe: Text in a ListView Pin
    Peterlip27-Feb-03 13:33
    Peterlip27-Feb-03 13:33 
    QuestionHow to break the 260 bytes limitations ? Pin
    KaЯl14-Jun-02 4:36
    KaЯl14-Jun-02 4:36 
    AnswerRe: How to break the 260 bytes limitations ? Pin
    deey26-Aug-02 15:38
    deey26-Aug-02 15:38 
    GeneralRe: How to break the 260 bytes limitations ? Pin
    KaЯl26-Aug-02 21:25
    KaЯl26-Aug-02 21:25 
    AnswerRe: How to break the 260 bytes limitations ? Pin
    Wicket_13-Feb-03 21:57
    sussWicket_13-Feb-03 21:57 
    GeneralRe: How to break the 260 bytes limitations ? Pin
    Mke5-Mar-03 7:19
    Mke5-Mar-03 7:19 
    GeneralRe: How to break the 260 bytes limitations ? Pin
    Phil Hamer8-Apr-03 17:10
    Phil Hamer8-Apr-03 17:10 
    AnswerRe: How to break the 260 bytes limitations ? Pin
    Tom Archer8-Aug-04 4:28
    Tom Archer8-Aug-04 4:28 
    GeneralRe: How to break the 260 bytes limitations ? Pin
    KaЯl8-Aug-04 5:11
    KaЯl8-Aug-04 5:11 
    GeneralProblem at AddItem() function Pin
    5-Jun-02 20:10
    suss5-Jun-02 20:10 
    GeneralRe: Problem at AddItem() function Pin
    5-Jun-02 21:22
    suss5-Jun-02 21:22 
    GeneralDon't forget to initialize m_nNextFree in the constructor Pin
    11-Jan-02 18:03
    suss11-Jan-02 18:03 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.