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



Comments

  • MrDrIng

    Posted by SoLaR on 11/02/2012 06:21am

    Sample code fails to compile on VC++ 2005! Also I do not see the point if you must change half of your code to implement this. why not simply do it your self. ie: create thread and start it. =.= DOH

    Reply
  • There is another method for filling childs item in a treectrl!!

    Posted by elmajdouli on 01/11/2005 10:10am

    the Get Childs Items callback. It's well documented issue in MSDN.

    Reply
  • OnSelchanged points to unknown structure Help!

    Posted by Legacy on 09/23/2002 12:00am

    Originally posted by: Vaclav

    I must be doing something wrong, but using On Selchanged message procesing does not retrieve expected item from the tree. What am I missing?

    Here is my code snippet:

    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
    // analyze pNMHDR
    if (pNMHDR->code == TVN_SELCHANGED)
    {
    TRACE ( " Item selected ");
    if((pNMTreeView->itemNew.mask & TVIF_TEXT) == TVIF_TEXT)

    pNMTreeView->itemNew.pszText;

    else
    TRACE( " \n Text mask %x invalid, should be %x ",(pNMTreeView->itemNew.mask & TVIF_TEXT),TVIF_TEXT );

    Reply
  • THANK YOU!!!

    Posted by Legacy on 03/01/2002 12:00am

    Originally posted by: Chian Yu

    I'm search for the CTreeView simple example for a long time! And I'm going to crazy! I have about 10 books about VC6.0 and MFC, but none of them have the answer! Thank God! And Thank You!

    I come from Taiwan, John is my english name.
    I'm so Happy now! Thanks!!!!

    Reply
  • i need window explorer in vc to download

    Posted by Legacy on 06/30/2001 12:00am

    Originally posted by: saquib khan

    i need window explorer in vc to download today
    

    Reply
  • Help!

    Posted by Legacy on 03/27/2001 12:00am

    Originally posted by: Ajit

    I have a user management dialog that has a tree control on the left and five tabs alongside, displaying information about the selected item. The tree control is being populated by a separate worker thread (like yours), which gets called when the dialog is being initialised. The reason for having a separate thread for loading of the users, is because the expected no of users runs in 10,000's and I don't want to wait to display the dialog until I've loaded all the users.

    Everything works fine, if an item is selected during the loading, the thread switches, displays the user information in the thread and get's back to the loading.

    The only problem is when I click on OK or Cancel in the dialog during the load, it crashes. I realised that the app switches thread's ( between Main & Worker Thread ) when I click on any other control, and gets back to the worker after completing the task. When I click on OK or Cancel, the dialog is supposed to be dismissed and it gets destroyed, now when my thread switches again, the Worker Thread is supposed to come to the place it left off, which now does not exist and crashes.

    I have a flag that will tell the thread that it should abort the search and exit normally, but it does not get called. Because the OK/Cancel does not return focus on the Thread Function. I tried ResumeThread, but it returns 0, which means that the thread was never suspended, hence cannot be restarted.

    Reply
  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds