Click here to Skip to main content
15,884,388 members
Articles / Desktop Programming / MFC

Tree Control with Bitmap Checkboxes Supported

Rate me:
Please Sign up or sign in to vote.
4.69/5 (9 votes)
24 Oct 20023 min read 154.5K   4.7K   38  
Adding a checkbox along with tree items
// XMLTree.cpp : implementation file
//

#include "stdafx.h"
#include "vs_treectrl.h"
#include <comdef.h>
#include "XMLTree.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CXMLTree

CXMLTree::CXMLTree()
{
	m_pXML=NULL;
}

CXMLTree::~CXMLTree()
{
}


BEGIN_MESSAGE_MAP(CXMLTree, CTreeCtrl)
	//{{AFX_MSG_MAP(CXMLTree)
		ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemexpanding)
	ON_WM_LBUTTONDOWN()
	ON_WM_KEYDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CXMLTree message handlers
void CXMLTree::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	HTREEITEM hItem = pNMTreeView->itemNew.hItem;
	/*MSXML::*/IXMLDOMElement* node = (/*MSXML::*/IXMLDOMElement*)/*GetTreeCtrl().*/GetItemData(hItem);
	HRESULT hr;

	*pResult = 0;
	
	// If m_bOptimizeMemory is false then we optimize by not adding and deleting 
	// already added items. The trade-off is memory exhausted!
	
	CWaitCursor waitCursor;     // This could take a while!
	/*GetTreeCtrl().*/LockWindowUpdate();
	if (pNMTreeView->action == TVE_EXPAND) {
		if (m_bOptimizeMemory == FALSE) {
			HTREEITEM hChildItem;
			if ((hChildItem = /*GetTreeCtrl().*/GetChildItem(hItem)) != NULL) {
				/*MSXML::*/IXMLDOMElement* childNode = 
					(/*MSXML::*/IXMLDOMElement*)/*GetTreeCtrl().*/GetItemData(hChildItem);
				if (childNode == NULL) {
					/*GetTreeCtrl().*/DeleteItem(hChildItem);
					/*MSXML::*/IXMLDOMNode* firstChild = NULL;
					hr = node->get_firstChild(&firstChild);
					if (SUCCEEDED(hr) && firstChild != NULL) {
						if (populateNode((/*MSXML::*/IXMLDOMElement*)firstChild, hItem) == FALSE) {
							*pResult = 1;            
						}
					}
				}
			}
		} else {
			deleteFirstChild(hItem);
			/*MSXML::*/IXMLDOMNode* firstChild = NULL;
			hr = node->get_firstChild(&firstChild);
			if (SUCCEEDED(hr) && firstChild != NULL) {
				if (populateNode((/*MSXML::*/IXMLDOMElement*)firstChild, hItem) == FALSE) {
					*pResult = 1;            
				}
			}
		}
	} else { // pNMTreeView->action == TVE_COLLAPSE
		if (m_bOptimizeMemory == TRUE) {
			deleteAllChildren(hItem);
			// Set button state.
			VARIANT_BOOL vtb;
			//if (node->hasChildNodes()) {
			node->hasChildNodes (&vtb);
			if(vtb)
			{
				int nImage, nSelectedImage;
				nImage = nSelectedImage = getIconIndex(node);
				HTREEITEM hChildItem = /*GetTreeCtrl().*/InsertItem(_T(""), nImage, 
					nSelectedImage, hItem);
				/*GetTreeCtrl().*/SetItemData(hChildItem, (DWORD)NULL);
			}
		}
	}
	/*GetTreeCtrl().*/UnlockWindowUpdate();
}


BOOL CXMLTree::loadXML(const CString &strPathName, const BOOL bOptimizeMemory /*= FALSE*/ ,const BOOL need_doc)
{
	/*MSXML::*/IXMLDOMDocument* document = NULL;
	/*MSXML::*/IXMLDOMParseError* parseError = NULL;
	/*MSXML::*/IXMLDOMElement* element = NULL;	

	DeleteAllItems();

	HRESULT hr;
	hr = CoInitialize(NULL);
	if (FAILED(hr)) {
		return FALSE;
	}

	hr = CoCreateInstance(/*MSXML::*/CLSID_DOMDocument, NULL, 
		CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, 
		/*MSXML::*/IID_IXMLDOMDocument, (LPVOID*)&document);
	if (!document) {
		return FALSE;
	}

	BSTR bstr = NULL;
	document->put_async(VARIANT_FALSE);
	bstr = strPathName.AllocSysString();
	VARIANT_BOOL varOkay;
	//= document->load(bstr);
	document->load (_variant_t(bstr),&varOkay);
	SysFreeString(bstr);
	
	BSTR nodeName;
	HTREEITEM hItem;	
	if (varOkay) {
		hr = document->get_documentElement(&element);
		if (FAILED(hr) || element == NULL) {
			MessageBox(_T("Empty document!"), _T("Error Loading XML"), MB_ICONWARNING);
			return FALSE;
		}
		element->get_nodeName(&nodeName);
		hItem = insertItem(element, CString(nodeName), ILI_ROOT, ILI_ROOT);	
		// Optional expand the root initially.
		Expand(hItem, TVE_EXPAND);
		Select(hItem, TVGN_CARET);
	} else {
		long line, linePos;
		BSTR reason = NULL;
		
		document->get_parseError(&parseError);
		parseError->get_errorCode(&hr);
		parseError->get_line(&line);
		parseError->get_linepos(&linePos);
		parseError->get_reason(&reason);
		
		CString strMsg;
		strMsg.Format(_T("Error 0x%.8X on line %d, position %d\r\nReason: %s"), 
			hr, line, linePos, CString(reason));
		MessageBox(strMsg, _T("Error Loading XML"), MB_ICONWARNING);
				
		SysFreeString(reason);
		return FALSE;
	}

	// Must initialize here.
	m_bOptimizeMemory = bOptimizeMemory;
	if ( need_doc)
	{
		m_pXML = document;
		m_URL = strPathName;
	}
	return TRUE;
}

// This function populates all siblings of the [in] node.
//
BOOL CXMLTree::populateNode(/*MSXML::*/IXMLDOMElement *node, const HTREEITEM &hParent)
{
	HRESULT hr = S_OK;
	BSTR nodeType, nodeName;
	HTREEITEM hItem;

	node->get_nodeTypeString(&nodeType);
	if (!wcscmp(nodeType, L"element")) {
		node->get_nodeName(&nodeName);
		hItem = insertItem(node, CString(nodeName), ILI_ELEMENT, ILI_ELEMENT, hParent);
		populateAttributes(node, hItem);
	} else if(!wcscmp(nodeType, L"text")) {
		node->get_text(&nodeName);
		hItem = insertItem(node, CString(nodeName), ILI_TEXT, ILI_TEXT, hParent);	
	} else if(!wcscmp(nodeType, L"comment")) {
		node->get_nodeName(&nodeName);
		hItem = insertItem(node, CString(nodeName), ILI_COMMENT, ILI_COMMENT, hParent);	
	} else {    // Handle more data types here if needed.
		node->get_nodeName(&nodeName);
		hItem = insertItem(node, CString(nodeName), ILI_OTHER, ILI_OTHER, hParent);	
	}
	
	/*MSXML::*/IXMLDOMNode* nextSibling = NULL;
	hr = node->get_nextSibling(&nextSibling);
	if (SUCCEEDED(hr) && nextSibling != NULL) {
		populateNode((/*MSXML::*/IXMLDOMElement*)nextSibling, hParent);
	}

	return TRUE;
}

BOOL CXMLTree::populateAttributes(/*MSXML::*/IXMLDOMElement *node, const HTREEITEM &hParent)
{
	HRESULT hr = S_OK;
	BSTR nodeName;
	HTREEITEM hItem;

	/*MSXML::*/IXMLDOMNamedNodeMap* namedNodeMap = NULL;
	hr = node->get_attributes(&namedNodeMap);
	if (SUCCEEDED(hr) && namedNodeMap != NULL) {
		long listLength;
		hr = namedNodeMap->get_length(&listLength);
		for(long i = 0; i < listLength; i++) {
			/*MSXML::*/IXMLDOMNode* listItem = NULL;
			hr = namedNodeMap->get_item(i, &listItem);
			listItem->get_nodeName(&nodeName);
			hItem = insertItem((/*MSXML::*/IXMLDOMElement*)listItem, 
				CString(nodeName), ILI_ATTRIBUTE, ILI_ATTRIBUTE, hParent);	
		}
	}

	return TRUE;
}

HTREEITEM CXMLTree::insertItem(/*MSXML::*/IXMLDOMElement *node, 
	const CString &nodeName, int nImage, int nSelectedImage, 
	HTREEITEM hParent /*= TVI_ROOT*/, HTREEITEM hInsertAfter /*= TVI_LAST*/)
{
	HTREEITEM hItem = /*GetTreeCtrl().*/InsertItem(nodeName, nImage, 
		nSelectedImage, hParent, hInsertAfter);	


	if ( GetWindowLong(m_hWnd ,GWL_STYLE) & TVS_CHECKBOXES )
	{	
		BOOL checked=FALSE;
		if ( hParent != TVI_ROOT)
			 checked = IsItemChecked(hParent);
		if ( checked )
			SetChecked(hItem);
		else
			SetUnchecked(hItem);
	}
	/*GetTreeCtrl().*/SetItemData(hItem, (DWORD)node);
	

	// Set button state.
	VARIANT_BOOL vtb;
	node->hasChildNodes(&vtb);
	if (vtb) {
		HTREEITEM hChildItem = /*GetTreeCtrl().*/InsertItem(_T(""), nImage, 
			nSelectedImage, hItem);
		/*GetTreeCtrl().*/SetItemData(hChildItem, (DWORD)NULL);
	}

	return hItem;
}

void CXMLTree::deleteFirstChild(const HTREEITEM &hItem)
{
	HTREEITEM hChildItem;
	if ((hChildItem = /*GetTreeCtrl().*/GetChildItem(hItem)) != NULL) {
		/*GetTreeCtrl().*/DeleteItem(hChildItem);
	}
}

 void CXMLTree::deleteAllChildren(const HTREEITEM &hItem)
 {
 	HTREEITEM hChildItem;
 	if ((hChildItem = /*GetTreeCtrl().*/GetChildItem(hItem)) == NULL) {
 		return;
 	}
 
 	do {
 		HTREEITEM hNextItem = /*GetTreeCtrl().*/GetNextSiblingItem(hChildItem);
 		/*GetTreeCtrl().*/DeleteItem(hChildItem);
 		hChildItem = hNextItem;
 	} while (hChildItem != NULL);
 }

int CXMLTree::getIconIndex(/*MSXML::*/IXMLDOMElement *node)
{
	BSTR nodeType;

	node->get_nodeTypeString(&nodeType);
	if (!wcscmp(nodeType, L"element")) {
		return ILI_ELEMENT;
	} else if(!wcscmp(nodeType, L"attribute")) {
		return ILI_ATTRIBUTE;
	} else if(!wcscmp(nodeType, L"text")) {
		return ILI_TEXT;
	} else if(!wcscmp(nodeType, L"comment")) {
		return ILI_COMMENT;
	}

	return ILI_OTHER;
}



BOOL CXMLTree::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
{
	// TODO: Add your specialized code here and/or call the base class
	
	return CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
}

BOOL CXMLTree::CreateTree(CRect rect,CWnd *parent, UINT image)
{
	if(!CTreeCtrl::Create (WS_TABSTOP | WS_CHILD | WS_BORDER|WS_THICKFRAME   
   | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES
   | TVS_DISABLEDRAGDROP
	,rect,parent,65535))
		return FALSE;
	if (image)
		SetImages (image);
	return TRUE;
}

void CXMLTree::SetImages(UINT resource)
{
	m_theImageList.Detach();	// For MDI
	if ( resource ==0 )
		m_theImageList.Create(IDB_BITMAP_TREE_ICONS, 16, 4, RGB(255, 255, 255));	
	else
		m_theImageList.Create (resource,16,4,RGB(255,255,255));
	SetImageList(&m_theImageList, TVSIL_NORMAL);

	m_imageState.Detach ();

	m_imageState.Create( IDB_STATE, 16, 1, RGB(255,255,255) );
	SetImageList( &m_imageState, TVSIL_STATE );


}

void CXMLTree::SaveXML(const CString &strFileName)
{
	CString fname = m_URL;
	if( !strFileName.IsEmpty ())
	{
		fname = strFileName;
	}
	m_pXML->save (_variant_t(fname));
}

IXMLDOMElement* CXMLTree::AddNewNode(CString nodeName, CString strValue)
{
	HTREEITEM item = GetSelectedItem ();
	/*MSXML::*/IXMLDOMElement *node=NULL;
	/*MSXML::*/IXMLDOMElement *new_node=NULL;
	if ( item )
	{
		node= (/*MSXML::*/IXMLDOMElement*)GetItemData (item);
		m_pXML->createElement (_bstr_t(nodeName),&new_node);
		if ( new_node)
		{
			//new_node->put_nodeValue (_variant_t(strValue));
			new_node->put_text (_bstr_t(strValue));
		}
		if ( node)
		{
			IXMLDOMNode *element=NULL;
			node->appendChild (new_node,&element);
			HTREEITEM new_item = insertItem(new_node,nodeName,1,1,item,TVI_LAST);
			SelectItem (new_item);
			return new_node;
		}

	}
	return NULL;
}

void CXMLTree::DeleteNode(CString strPath)
{
	HTREEITEM item = GetSelectedItem ();
	IXMLDOMElement *node=NULL;
	//if (strPath.IsEmpty ())
	if( item)
	{
		 node= (IXMLDOMElement*)GetItemData (item);
		if ( node )
		{
			IXMLDOMNode *parent=NULL;
			IXMLDOMNode *old_node=NULL;
			node->get_parentNode (&parent);		
			if ( parent)
				parent->removeChild (node,&old_node);
		}
		this->DeleteItem (item);
	}
}

void CXMLTree::AddAttribute(IXMLDOMElement *element,CString strName, CString strValue)
{
	IXMLDOMAttribute *attribute=NULL;
	m_pXML->createAttribute (_bstr_t(strName),&attribute);
	IXMLDOMAttribute *element1=NULL;
	element->setAttributeNode  (attribute,&element1);
	element->setAttribute (_bstr_t(strName),_variant_t(strValue));

}
void CXMLTree::AddXMLToNode(HTREEITEM item ,IXMLDOMElement *element,CString strXML)
{
	IXMLDOMNode *ele_new=NULL;
	IXMLDOMDocument *document=NULL;
	IXMLDOMElement *origin=NULL;
	HRESULT hr;
	if ( item )
	{

		BSTR bstr;
		bstr = strXML.AllocSysString ();
		hr = CoCreateInstance(CLSID_DOMDocument, NULL, 
		CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, 
		IID_IXMLDOMDocument, (LPVOID*)&document);
		if (!document) 	return ;
		document->put_async(VARIANT_FALSE);
		VARIANT_BOOL varOkay;
		document->loadXML (bstr,&varOkay);
		SysFreeString(bstr);
		if (varOkay) 
		{
			hr = document->get_documentElement(&origin);
			if (SUCCEEDED(hr))
			{
				hr=origin->cloneNode (VARIANT_TRUE,&ele_new);
				if (SUCCEEDED(hr))
				{
					IXMLDOMNode *old_node=NULL;
					hr= element->appendChild (ele_new,&old_node);
					if (SUCCEEDED(hr))
					{
					   BSTR name;
					   ele_new->get_nodeName (&name);
					   HTREEITEM new_item =insertItem((IXMLDOMElement*)ele_new,CString(name),0,0,item,TVI_LAST);
					   SelectItem(new_item);
					}


				}

			}
		}
}

}

	

void CXMLTree::OnLButtonDown(UINT nFlags, CPoint point) 
{
	UINT uFlags=0;
	HTREEITEM hti = HitTest(point,&uFlags);

	if( uFlags & TVHT_ONITEMSTATEICON )
	{
		ToggleItemState(hti);
//		int iImage = GetItemState( hti, TVIS_STATEIMAGEMASK )>>12;
//		SetItemState( hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1), 
//					TVIS_STATEIMAGEMASK );
		return;
	}
	
	CTreeCtrl::OnLButtonDown(nFlags, point);
}

void CXMLTree::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	if( nChar == VK_SPACE )
	{
		HTREEITEM hti = GetSelectedItem();
		ToggleItemState(hti);
		//int iImage = GetItemState( hti, TVIS_STATEIMAGEMASK )>>12;
		//SetItemState( hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1),
		//			TVIS_STATEIMAGEMASK );
		return;
	}
	CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}

BOOL CXMLTree::IsItemChecked(HTREEITEM hItem)
{
	return GetItemState( hItem, TVIS_STATEIMAGEMASK )>>12 == 2;
}



HTREEITEM CXMLTree::GetFirstCheckedItem()
{
	for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextItem( hItem ) )
		if ( IsItemChecked(hItem) )
			return hItem;

	return NULL;

}

HTREEITEM CXMLTree::GetNextCheckedItem(HTREEITEM hItem)
{
	for ( hItem = GetNextItem( hItem ); hItem!=NULL; hItem = GetNextItem( hItem ) )
		if ( IsItemChecked(hItem) )
			return hItem;

	return NULL;

}

HTREEITEM CXMLTree::GetPrevCheckedItem(HTREEITEM hItem)
{
	for ( hItem = GetPrevItem( hItem ); hItem!=NULL; hItem = GetPrevItem( hItem ) )
		if ( IsItemChecked(hItem) )
			return hItem;

	return NULL;

}

void CXMLTree::SetChecked(HTREEITEM hItem)
{
	SetItemState( hItem, INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK );

}
// GetNextItem	- Get next item as if outline was completely expanded
// Returns		- The item immediately below the reference item
// hItem		- The reference item

HTREEITEM CXMLTree::GetNextItem(HTREEITEM hItem)
{

	HTREEITEM	hti;

	if( ItemHasChildren( hItem ) )
		return GetChildItem( hItem );		// return first child
	else{
		// return next sibling item
		// Go up the tree to find a parent's sibling if needed.
		while( (hti = GetNextSiblingItem( hItem )) == NULL ){
			if( (hItem = GetParentItem( hItem ) ) == NULL )
				return NULL;
		}
	}
	return hti;


}

HTREEITEM CXMLTree::GetPrevItem(HTREEITEM hItem)
{
	HTREEITEM hti;

	// Return a previous sibling item if it exists
	if( hti = GetPrevSiblingItem(hItem) )
		return hti;		
	else
		// No sibling, return the parent 
		return GetParentItem(hItem);

}

void CXMLTree::ToggleItemState(HTREEITEM hti)
{
	int iImage = GetItemState( hti, TVIS_STATEIMAGEMASK )>>12;
	SetItemState( hti, INDEXTOSTATEIMAGEMASK(iImage == 1 ? 2 : 1), 
				TVIS_STATEIMAGEMASK );
	if ( ItemHasChildren(hti))
	{
		HTREEITEM htiChild = this->GetChildItem (hti);
		if (htiChild)
			ToggleItemState(htiChild);
		else
			return ;
		HTREEITEM htiSibling = GetNextSiblingItem (htiChild);
		while (htiSibling )
		{
			ToggleItemState(htiSibling);
			htiSibling = GetNextSiblingItem(htiSibling);
		}


	}

}

void CXMLTree::SetUnchecked(HTREEITEM hti)
{
	SetItemState( hti, INDEXTOSTATEIMAGEMASK(1), TVIS_STATEIMAGEMASK );

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


Written By
Software Developer (Senior)
China China
I'm write program from 1990. My research field is CAG,CAD and Image processing. I select C/C++, ASP, Java, XML as my usaully developing tools. Occasional , write code in Delphi and VB. I'm using Visual C++ from 1996. If you have anything unclear, e-mail to :zhou_cn123@sina.com Software Engineering and CAD is my mainly research program.

You also can reach me on msn: zhoujohnson@hotmail.com

Comments and Discussions