这篇文章,目前(2008.10.17)我主要借鉴怎样把IHTMLDOMNode与IHTMLDOMChildrenCollection相连接并使用。
- 用来显示web page的DOM tree的类,继承自CTreeCtrl --source file
- // DOMTree.cpp : implementation file
- #include "stdafx.h"
- #include "DOMTree.h"
- #include <queue>
- #define IDM_EXPANDALL 10001
- #define IDM_COLLAPSEALL 10002
- IMPLEMENT_DYNAMIC (CDOMTree, CTreeCtrl)
- BEGIN_MESSAGE_MAP (CDOMTree, CTreeCtrl)
- ON_NOTIFY_REFLECT(TVN_DELETEITEM, OnTvnDeleteitem)
- ON_WM_DESTROY()
- ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnTvnSelchanged)
- ON_COMMAND(IDM_EXPANDALL,OnExpandAll)
- ON_COMMAND(IDM_COLLAPSEALL,OnCollapseAll)
- ON_WM_RBUTTONDOWN()
- END_MESSAGE_MAP()
- void CDOMTree::OnTvnDeleteitem(NMHDR *pNMHDR , LRESULT *pResult)
- {
- LPNMTREEVIEW pNMTreeView = reinterpret_cast <LPNMTREEVIEW>(pNMHDR);
- TVITEM* pItem = &pNMTreeView->itemOld ;
- if ( pItem && pItem->lParam)
- {
- IUnknown* pNodeUnknown = reinterpret_cast<IUnknown*>(pItem-> lParam);
- #if defined(_DEBUG)
- long relcnt = pNodeUnknown->Release(); // for AddRef when added to the tree item data
- ATLASSERT( relcnt == 0);
- #else
- pNodeUnknown->Release ();
- #endif
- }
- *pResult = 0;
- }
- void CDOMTree::OnDestroy()
- {
- CTreeCtrl::OnDestroy();
- }
- void CDOMTree::UpdateDOM(IHTMLDocument2* pDoc )
- {
- SetRedraw( FALSE);
- DeleteAllItems();
- m_spDoc2 = pDoc;
- PrepareDOMTree();
- SetRedraw( TRUE);
- }
- //a breadth first tree creating algorithm
- bool CDOMTree::PrepareDOMTree()
- {
- CComQIPtr<IHTMLDocument3> spDoc3 = m_spDoc2;
- bool bRet = spDoc3 != NULL;
- if( !bRet ){
- ATLASSERT(0);
- return bRet;
- }
- CComPtr <IHTMLElement> spRootElement;
- bRet = SUCCEEDED( spDoc3-> get_documentElement( &spRootElement));
- if( !bRet ) return bRet;
- CComQIPtr <IHTMLDOMNode> spRootNode = spRootElement ;
- bRet = spRootNode != NULL;
- if ( !bRet ) return bRet;
- std:: queue<HTREEITEM> queue_tree_nodes;
- std::queue<CComQIPtr< IHTMLDOMNode> > queue_DOMNodes;
- queue_DOMNodes .push(spRootNode);
- queue_tree_nodes.push(TVI_ROOT);
- while( !queue_tree_nodes.empty() )
- {
- CComQIPtr<IHTMLDOMNode> spDOMNode=queue_DOMNodes.front();
- HTREEITEM parent=queue_tree_nodes.front ();
- queue_DOMNodes.pop();
- queue_tree_nodes.pop();
- HTREEITEM thisnode=InsertDOMNode(spDOMNode, parent);
- CComPtr<IDispatch> spCollectionDispatch;
- if ( SUCCEEDED( spDOMNode->get_childNodes( & spCollectionDispatch)))
- {
- long numChildren = 0;
- CComQIPtr<IHTMLDOMChildrenCollection> spCollection = spCollectionDispatch;
- if (!! spCollection)
- {
- spCollection->get_length( & numChildren);
- for ( long i = 0; i < numChildren; i++)
- {
- CComPtr<IDispatch> spItemDispatch;
- spCollection->item( i , &spItemDispatch);
- if ( !!spItemDispatch)
- {
- CComQIPtr<IHTMLDOMNode> spItemNode = spItemDispatch;
- if ( !!spItemNode)
- {
- queue_DOMNodes.push(spItemNode);
- queue_tree_nodes.push(thisnode);
- }
- }
- }
- }
- }
- }
- return bRet;
- }
- HTREEITEM CDOMTree::InsertDOMNode( IHTMLDOMNode* pINode, HTREEITEM hparent)
- {
- CComPtr<IDispatch> spCollectionDispatch;
- CComPtr<IHTMLDOMNode> spNode(pINode);
- CComBSTR NodeName;
- TV_INSERTSTRUCT tvis;
- tvis.hParent = hparent;
- tvis.hInsertAfter = TVI_LAST;
- tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
- if ( SUCCEEDED( spNode->get_nodeName( & NodeName)))
- {
- CString strNodeName( NodeName);
- tvis.item.pszText= strdup(strNodeName);
- }
- // Need to AddRef because we'll be keeping interface pointer as treeview item data
- pINode->AddRef();
- tvis.item.lParam = reinterpret_cast<LPARAM>(pINode);
- HTREEITEM hthisItem = InsertItem( &tvis );
- if ( hthisItem == NULL)
- {
- pINode->Release();
- return NULL;
- }
- return hthisItem;
- }
- void CDOMTree::OnTvnSelchanged(NMHDR *pNMHDR , LRESULT *pResult)
- {
- LPNMTREEVIEW pNMTreeView = reinterpret_cast <LPNMTREEVIEW>(pNMHDR);
- TVITEM* pItem = &pNMTreeView->itemNew ;
- IHTMLDOMNode* pDOMNode= reinterpret_cast<IHTMLDOMNode*>(pItem-> lParam);
- *pResult = 0;
- }
- void CDOMTree:: OnExpandAll()
- {
- ExpandAll();
- }
- void CDOMTree::OnCollapseAll()
- {
- CollapseAll();
- }
- void CDOMTree::OnRButtonDown(UINT nFlags , CPoint point)
- {
- CMenu context;
- context.CreatePopupMenu();
- ClientToScreen(&point);
- context.AppendMenu(MF_STRING|MF_ENABLED ,IDM_EXPANDALL,_T("Expand All"));
- context.AppendMenu(MF_STRING|MF_ENABLED ,IDM_COLLAPSEALL,_T("Collapse All"));
- context.TrackPopupMenu( TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, point.x, point.y, this);
- CTreeCtrl::OnRButtonDown(nFlags, point);
- }
- void CDOMTree::ExpandAll()
- {
- HTREEITEM hCurrent = GetSelectedItem();
- if ( !hCurrent)
- hCurrent = GetRootItem();
- if ( hCurrent)
- {
- SetRedraw( FALSE);
- // When expanding we must expand parent before its children
- unsigned int flags = DOPARENTFIRST;
- DoForItemAndChildren( hCurrent, ExpandItem, flags);
- EnsureVisible( hCurrent);
- SetRedraw( TRUE);
- }
- }
- // Recursive helper that calls a method, that param fp points to, for a hParent node
- // and all of its children, its children's children, etc.
- // This is one of few cases where I used ->* notation for method call through a pointer
- void CDOMTree::DoForItemAndChildren( HTREEITEM hParent, FOREACHITEM fp, unsigned int flags /*= 0*/)
- {
- // DOPARENTFIRST flag tells us if we want fp to be called on parent node first, then children, or viceversa
- if ( flags & DOPARENTFIRST)
- {
- if ( !( flags & SKIPTOPPARENT))
- (this->*fp)( hParent);
- }
- HTREEITEM hChild = GetChildItem(hParent);
- while ( hChild)
- {
- DoForItemAndChildren( hChild, fp, ( flags & DOPARENTFIRST) ? DOPARENTFIRST : 0);
- hChild = GetNextSiblingItem( hChild);
- };
- if ( !(flags & DOPARENTFIRST))
- {
- if ( !( flags & SKIPTOPPARENT))
- (this->*fp)( hParent);
- }
- }
- void CDOMTree::CollapseAll()
- {
- HTREEITEM hCurrent = GetSelectedItem();
- if ( !hCurrent)
- hCurrent = GetRootItem();
- if ( hCurrent)
- {
- SetRedraw( FALSE);
- Expand( hCurrent, TVE_COLLAPSE);
- SetRedraw( TRUE);
- }
- }