CTreeView extension for data population in a separate thread

When would you need this class:

You might be interested in a tree control which populates it’s
data in a separate thread. This is usually required if you have some fixed data to be
shown in tree view, but it takes a while to populate. The following class would be quite
useful if you have such a requirement. At the moment I have kept the implementation quite
simple, but one can add/modify it according to requirement.

When can you use this class:

  1. You have some fixed data to be populated in the tree. You can
    always do modifications later but the thread works only on some fixed data available.

  2. Given a parent item, you can always supply all it’s children.

How does it work:

I have made this a template class, so that you can associate
your own data as necessary. It takes a structure you define as a parameter. The structure
should contain information required for tree population. Each instance of this structure
would represent one tree node. The class should be in the base class list of your
CtreeView derived class. I name it CtreeViewExt. So your class declaration would
look like this.

class CMyTreeView : public CTreeView, public CtreeViewExt<T>
{
	//...
};

The T parameter would be the structure you define. Please note that the structure should be STL list container
compliant.

The class CtreeViewExt has
a member function which starts off a new thread to collect and populate data. You would
call this member function inside your OnInitialUpdate(). It then asks you to
provide data through two pure virtual member functions , GetRootItems() and GetChildItems(),
which you must override. You need to override one more pure virtual function, the familiar
GetTreeCtrl(), which just returns CtreeView::GetTreeCtrl().

The class will periodically send
a user defined message, WM_DATAAVAILABLE, to your tree view class for which you should
have a message map as follows:

	ON_MESSAGE(WM_DATAAVAILABLE, OnDataAvailable)

The message map function would just call the CtreeViewExt::OnDataAvailable() which populates the control.

How to use CtreeViewExt:

Step 1: Define a data structure, say MYDATA, which is STL list<> class compliant and
which represents data specific to tree items.

Step 2: Add CtreeViewExt to the base class list of your
CtreeView based class as follows:

	class CMyTreeView : public CTreeView, public CtreeViewExt<T>
	{
		//...
	};

Step 3: Add messagemap entry for WM_DATAAVAILABLE in your derived class as follows:

	// inside header fil
	afx_msg LONG OnDataAvailable(WPARAM wParam, LPARAM lParam);
	// Inside implementaion fil
	BEGIN_MESSAGE_MAP(CMyTreeView, CTreeView)
	//...
	ON_MESSAGE(WM_DATAAVAILABLE, OnDataAvailable)
	END_MESSAGE_MAP()

	LONG CMyTreeView::OnDataAvailable(WPARAM wParam, LPARAM lParam)
	{
		return CTreeViewExt< MYDATA >::OnDataAvailable(wParam, lParam);
	}

Step 4:Override GetTreeCtrl() as follows:

	CTreeCtrl& GetTreeCtrl() const
	{
		return CTreeView::GetTreeCtrl();
	}

Step 5: Override pure virtual functions GetRootItems() and GetRootItems()
specific to your application.

Step 6: Call CtreeViewExt< MYDATA> ::OnIntialUpdate()in your derived
class as follows:

	void CMyTreeView::OnInitialUpdate()
	{
		CTreeView::OnInitialUpdate();
CTreeViewExt<MYDATA>::OnInitialUpdate(); }

That’s it!

The class declaration is as follows:

#if !defined(AFX_BASETREEVIEW_H__FA327061_63AC_11D2_89CB_D1CE14573F5F__INCLUDED_)
#define AFX_BASETREEVIEW_H__FA327061_63AC_11D2_89CB_D1CE14573F5F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 100
// BaseTreeView.h : header fil
/
#include 
using namespace std;

////////////////////////////////////////////////////////////////////////////
// CTreeViewExt vie
#define	WM_DATAAVAILABLE	WM_USER+101

// type T specifies any used defined structure specific to the tree dat
template<class T>
class CTreeViewExt
{
protected:

	virtual ~CTreeViewExt() {}

	struct TreeData
	{
	public:
		unsigned short		m_nImage; 	// Image index in case you provide image
							// for tree item
		CString			m_strText; 	// Text for the ite
		T			m_Data; 	// Data
		HTREEITEM		m_hHandle; 	// This sometimes refers to handle of
							// the tree item of this object and
							// otherwise to the paren
	public:
		TreeData() : m_nImage(-1),
			m_hHandle(NULL)
		{
		}

		bool operator==(const TreeData& Data)
		{
			return m_Data == Data.m_Data;
		}
	};

protected:
	list		m_ItemList;

public:
	// OnInitialUpdate starts a new thread with this functio
	static UINT InitialThreadProc(LPVOID pData)
	{
		ASSERT(pData!=NULL);
		CTreeViewExt* pThis = static_cast(pData);
		ASSERT(pThis!=NULL);
		return pThis->ThreadProc();
	}
	// The real thread functio
	UINT ThreadProc()
	{
		// remember to call CoInitialize() here if you are calling COM function
		GetRootItems(m_ItemList);
		GetTreeCtrl().SendMessage(WM_DATAAVAILABLE,0,0);
		list::iterator it, itTemp, itEnd, itInner;

		// here we start adding entries to the list in a loop and periodicall
		// sending message to out control to populate the dat
		// list intially has entries only for the root items and then it grow
		// as GetChildItem() returns more entrie

		itEnd = m_ItemList.end();
		itEnd--;
		for(it = m_ItemList.begin(); it!=m_ItemList.end(); )
		{
			list List;
			GetChildItems(it,List);
			//adjust paren
			for(itInner = List.begin(); itInner!=List.end(); ++itInner)
				itInner->m_hHandle = it->m_hHandle;

			// append at the end of the lis
			m_ItemList.insert(m_ItemList.end(),List.begin(),List.end());

			itTemp = it;
			++it;

			if(itTemp==itEnd)
			{
				m_ItemList.erase(itTemp);
				GetTreeCtrl().SendMessage(WM_DATAAVAILABLE,0,0);
				itEnd = m_ItemList.end();
				itEnd--;
			}
			else
			{
				m_ItemList.erase(itTemp);
			}
		}
		return 1;
	}

	virtual void GetRootItems(list& List) = 0;
	virtual void GetChildItems(list::iterator it, list& List) = 0;
	virtual CTreeCtrl& GetTreeCtrl() const = 0;

	// just populate the tree. You can modify this function as you see fit to you
	// application. Note that this function gets called in the main threa
	LONG OnDataAvailable(WPARAM wParam, LPARAM lParam)
	{
		list::iterator it;
		HTREEITEM hItem;
		TV_ITEM tvI;
		TV_INSERTSTRUCT tvIns;
		HTREEITEM hParent = NULL, hPrev = NULL;
		bool bFirst = true;

		for(it = m_ItemList.begin(); it!=m_ItemList.end();++it)
		{
			hParent = it->m_hHandle;
			if(bFirst)
			{
				hPrev = hParent;
				bFirst = false;
			}

			tvI.mask = TVIF_TEXT  | TVIF_IMAGE | TVIF_SELECTEDIMAGE| TVIF_CHILDREN;
			tvI.pszText = (LPSTR)(LPCTSTR)it->m_strText;
			tvI.cchTextMax = it->m_strText.GetLength();
			tvI.iImage = it->m_nImage;
			tvI.iSelectedImage = it->m_nImage;

			tvIns.item = tvI;
			tvIns.hInsertAfter = TVI_SORT;
			tvIns.hParent = it->m_hHandle;
			tvIns.item.cChildren = 1 ;

			hItem = GetTreeCtrl().InsertItem(&tvIns);

			if(hParent!=hPrev)
			{
				GetTreeCtrl().Expand(hPrev,TVE_EXPAND);
				hPrev = hParent;
			}
			else
			{
				//hPrev = hParent
			}

			it->m_hHandle = hItem;
		}

		if(hPrev)
			GetTreeCtrl().Expand(hPrev,TVE_EXPAND);
		return 1;
	}


// Attribute
public:

// Operation
public:
	 void OnInitialUpdate()
	{
		AfxBeginThread(InitialThreadProc, (LPVOID)this,THREAD_PRIORITY_LOWEST);
	}



// Implementatio
protected:


};

////////////////////////////////////////////////////////////////////////////
// CTreeViewEx



//{{AFX_INSERT_LOCATION}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line

#endif // !defined(AFX_BASETREEVIEW_H__FA327061_63AC_11D2_89CB_D1CE14573F5F__INCLUDED_

Download demo project – [28.9] KB

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read