Download demo project - 50
Kb 
Introduction
Recently I was required to display a very large
XML file in a tree view, I tried to implement like "virtual list
view" which displays whatever is visible in a view (when the user scrolls).
After brainstorm and some web surfing I remembered windows explorer. I'm not
sure how they implement it exactly but I guess they do same method as mine
which expand any tree node on the fly i.e. don't populate the whole tree at
start-up. If you don't do this, populate the whole tree for a 10 MB XML
file size takes up to 45 minutes on a fast machine! doing my way will takes
only approximately 3-5 seconds! The trick really is how to find out the
relationship and info of any tree node and associated DOM node any where any
time.
To incorporate this into your app, either derive your class from CXmlTreeView
or add appropriate message handlers and all helper methods to your own class.
Note:
- It'd be easier if you know how to incorporate XML parser into your app
(this article uses Microsoft XML parser because of its available!), however,
it's not a requirement.
- You can apply the same method to any application-specific value instead of
DOM node to populate a tree control
- You can easily change this class to use as a tree control instead.
The first you need to add a notify message handler for TVN_ITEMEXPANDING
which
notifies a tree view control's parent window that a parent item's list of
child items is about to expand or collapse.
void CXmlTreeView::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;
CWaitCursor waitCursor;
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
{
if (m_bOptimizeMemory == TRUE)
{
deleteAllChildren(hItem);
if (node->hasChildNodes())
{
int nImage, nSelectedImage;
nImage = nSelectedImage = getIconIndex(node);
HTREEITEM hChildItem = GetTreeCtrl().InsertItem(_T(""), nImage,
nSelectedImage, hItem);
GetTreeCtrl().SetItemData(hChildItem, (DWORD)NULL);
}
}
}
GetTreeCtrl().UnlockWindowUpdate();
}
The key in this function is the calls to GetItemData(..)
and populateNode(..)
.
GetItemData(..)
will retrieve the DOM node value associated with the specified item so we can
use this DOM node to figure out the relationship with the rest.
populateNode(..)
will only populate all siblings of the [in] node which is the child node of
the current node.
m_bOptimizeMemory
is
used to optimize memory by delete all children when collapse or not.
This value is set to false by default when you can loadXML(..)
.
[in] node.
BOOL CXmlTreeView::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 {
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;
}
The key in this function is the call to helper function insertItem(..) which
set DOM node value associated with the specified tree item.
HTREEITEM CXmlTreeView::insertItem(MSXML::IXMLDOMElement *node,
const CString &nodeName, int nImage, int nSelectedImage,
HTREEITEM hParent , HTREEITEM hInsertAfter )
{
HTREEITEM hItem = GetTreeCtrl().InsertItem(nodeName, nImage,
nSelectedImage, hParent, hInsertAfter);
GetTreeCtrl().SetItemData(hItem, (DWORD)node);
if (node->hasChildNodes()) {
HTREEITEM hChildItem = GetTreeCtrl().InsertItem(_T(""), nImage,
nSelectedImage, hItem);
GetTreeCtrl().SetItemData(hChildItem, (DWORD)NULL);
}
return hItem;
}
Please don't send emails but post here if you have
any questions regarding this article.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.