65.9K
CodeProject is changing. Read more.
Home

Dynamically re-creating a list box

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.72/5 (18 votes)

Jul 26, 2002

CPOL

2 min read

viewsIcon

131014

Function to recreate a listbox at run-time to allow new styles, preserving its data, and selections

Introduction

This article is a companion article to my article Dynamically re-creating a combo box. Similarly to the combo box, I frequently find myself needing to switch a list box to or from sorted, or to or from multi select. As with the combo box, changing these styles after creation is something that a listbox does not support. The function presented here will recreate the list box so that style changes take effect. The function will retain all list items, with item data, and the currently selected items.

When the style is modified on a listbox to include or remove the LBS_SORT for example, it can be seen (by using GetStyle(), or using a tool such as Spy++) that the control does in fact have the new style, and yet the control's behaviour has not changed. The function here simply gets the current info from the control, i.e. styles, font etc, and recreates the control using these values.

How to use

I originally had a CListBox-derived class which one member function to recreate the controls, but decided that this was more trouble than it was worth, as I usually use controls derived from other bases. Therefore I have presented this as a single function, which should be included in a global library file, so that you can invoke it on any listbox at any time.

In order to change the style of a listbox, you should perform the usual method of setting the new style e.g.

list.ModifyStyle(0, LBS_SORT);

and then after than call your should call:

RecreateListBox(&list)

The function takes an optional void pointer, lpParam, which is passed to CreateEx on recreating the control. If you have special requirements when creating your controls, and you normally pass in some data for the creation parameters, then you should pass the same info in here. Most developers can simply ignore this parameter.

The Function

The function to recreate the listbox is below:

// recreate the list box by copying styles etc, and list items
// and applying them to a newly created control
BOOL RecreateListBox(CListBox* pList, LPVOID lpParam/*=NULL*/)
{
	if (pList == NULL)
		return FALSE;
	if (pList->GetSafeHwnd() == NULL)
		return FALSE;

	CWnd* pParent = pList->GetParent();
	if (pParent == NULL)
		return FALSE;

	// get current attributes
	DWORD dwStyle = pList->GetStyle();
	DWORD dwStyleEx = pList->GetExStyle();
	CRect rc;
	pList->GetWindowRect(&rc);
	pParent->ScreenToClient(&rc);	// map to client co-ords
	UINT nID = pList->GetDlgCtrlID();
	CFont* pFont = pList->GetFont();
	CWnd* pWndAfter = pList->GetNextWindow(GW_HWNDPREV);

	// create the new list box and copy the old list box items 
	// into a new listbox along with each item's data, and selection state
	CListBox listNew;
	if (! listNew.CreateEx(dwStyleEx, _T("LISTBOX"), _T(""), dwStyle, 
                                rc, pParent, nID, lpParam))
	  return FALSE;
	listNew.SetFont(pFont);
	int nNumItems = pList->GetCount();
	BOOL bMultiSel = (dwStyle & LBS_MULTIPLESEL || dwStyle & LBS_EXTENDEDSEL);
	for (int n = 0; n < nNumItems; n++)
	{
		CString sText;
		pList->GetText(n, sText);
		int nNewIndex = listNew.AddString(sText);
		listNew.SetItemData(nNewIndex, pList->GetItemData(n));
		if (bMultiSel && pList->GetSel(n))
			listNew.SetSel(nNewIndex);
	}
	if (! bMultiSel)
	{
		int nCurSel = pList->GetCurSel();
		if (nCurSel != -1)
		{
			CString sSelText;
			// get the selection in the old list
			pList->GetText(nCurSel, sSelText);
			// now find and select it in the new list
			listNew.SetCurSel(listNew.FindStringExact(-1, sSelText));
		}
	}
	// destroy the existing window, then attach the new one
	pList->DestroyWindow();
	HWND hwnd = listNew.Detach();
	pList->Attach(hwnd);

	// position correctly in z-order
	pList->SetWindowPos(pWndAfter == NULL ? &CWnd::wndBottom
                                 : pWndAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

	return TRUE;
}

See Also

History

  • Version 1 - 26 Jul 2002 - First version
  • Version 2 - 30 Jul 2002 - modified to include changes suggested by Jean-Michel LE FOL and Davide Zaccanti