CMFCShellTreeCtrl控件的效率改进

来源:互联网 发布:什么是谷歌seo 编辑:程序博客网 时间:2024/04/30 00:40

VS2010新增加(相较于VC6)了一个CMFCShellTreeCtrl类,说实话,这个类确实很好,但是有一点你会发现,在展开某些节点的时候可能会很慢很慢。这严重影响了效率。为什么呢?很长一段时间,一直百思不得其解!甚至抓狂!原来问题出现在一个很小的函数上。

HRESULT CMFCShellTreeCtrl::EnumObjects(HTREEITEM hParentItem, LPSHELLFOLDER pParentFolder, LPITEMIDLIST pidlParent){ASSERT_VALID(this);ASSERT_VALID(afxShellManager); LPENUMIDLIST pEnum = NULL; HRESULT hr = pParentFolder->EnumObjects(NULL, m_dwFlags, &pEnum);if (FAILED(hr) || pEnum == NULL){return hr;} LPITEMIDLIST pidlTemp;DWORD dwFetched = 1; // Enumerate the item's PIDLs:while (SUCCEEDED(pEnum->Next(1, &pidlTemp, &dwFetched)) && dwFetched){TVITEM tvItem;ZeroMemory(&tvItem, sizeof(tvItem)); // Fill in the TV_ITEM structure for this item:tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN; // AddRef the parent folder so it's pointer stays valid:pParentFolder->AddRef(); // Put the private information in the lParam:LPAFX_SHELLITEMINFO pItem = (LPAFX_SHELLITEMINFO)GlobalAlloc(GPTR, sizeof(AFX_SHELLITEMINFO));ENSURE(pItem != NULL); pItem->pidlRel = pidlTemp;pItem->pidlFQ = afxShellManager->ConcatenateItem(pidlParent, pidlTemp); pItem->pParentFolder = pParentFolder;tvItem.lParam = (LPARAM)pItem; CString strItem = OnGetItemText(pItem);tvItem.pszText = strItem.GetBuffer(strItem.GetLength());tvItem.iImage = OnGetItemIcon(pItem, FALSE);tvItem.iSelectedImage = OnGetItemIcon(pItem, TRUE); //问题出现在这里,接下来要检查文件的属性,判断是否有子文件夹。正常情况下我们其实是不需要这么多属性的,前两个足够了SFGAO_HASSUBFOLDER 和 SFGAO_FOLDER 。将后面的其他属性全部屏蔽,再运行程序时,就会发现很快了。// Determine if the item has children:/*DWORD dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_DISPLAYATTRMASK | SFGAO_CANRENAME | SFGAO_FILESYSANCESTOR;*/DWORD dwAttribs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER ; pParentFolder->GetAttributesOf(1, (LPCITEMIDLIST*) &pidlTemp, &dwAttribs);tvItem.cChildren = (dwAttribs & (SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR)); // Determine if the item is shared:if (dwAttribs & SFGAO_SHARE){tvItem.mask |= TVIF_STATE;tvItem.stateMask |= TVIS_OVERLAYMASK;tvItem.state |= INDEXTOOVERLAYMASK(1); //1 is the index for the shared overlay image} // Fill in the TV_INSERTSTRUCT structure for this item:TVINSERTSTRUCT tvInsert; tvInsert.item = tvItem;tvInsert.hInsertAfter = TVI_LAST;tvInsert.hParent = hParentItem; InsertItem(&tvInsert);dwFetched = 0;} pEnum->Release();return S_OK;}



2014/10/16 :今天有网友提问如何在MFC程序中使用CMFCShellTreeCtrl,其实很简单。首先新建一个基于CMFCShellTreeCtrl的MFC类,然后将新建的类和控件绑定。CMFCShellTreeCtrl有几个比较重要的虚函数:
/* 获取Item的文字*/
virtual CString OnGetItemText(LPAFX_SHELLITEMINFO pItem);
/* 获取Item的图标 */
virtual int OnGetItemIcon(LPAFX_SHELLITEMINFO pItem, BOOL bSelected);
/* 枚举目录下所有的文件 */
virtual HRESULT EnumObjects(HTREEITEM hParentItem, LPSHELLFOLDER pParentFolder, LPITEMIDLIST pidlParent);
其中第3个函数就是上文提到的可以修改的函数。但是有一点需要注意的是,MFC框架内使用了一个全局变量:
extern CShellManager* afxShellManager;
这个变量并没有被MFC框架导出,所以在我们自己的源码中是不能引用这个变量的。解决这个问题也很简单。
afxShellManager被定义在afxshellmanager.cpp文件中,而且要求全局范围内只有一个变量。
CShellManager* afxShellManager = NULL;
CShellManager::CShellManager() {
// 实际上要求CShellManager在全局范围内只有一个变量
ENSURE(afxShellManager == NULL);
afxShellManager = this;
...
}
所以我们在MFC框架生成的App类的InitInstance函数中能够看到:
 CShellManager pShellManager = new CShellManager;
delete pShellManager;
我们只需要将此处局部性质的pShellManager提高到全局范围内,然后在程序中使用它。
我们说使用它,不是说将所有使用afxShellManager变量的地方全部改为pShellManager,而是仅限于我们自己的项目代码,MFC框架的源代码是不能被更改的,而且是不应被更改的

原文链接:http://blog.163.com/lvan100@yeah/blog/static/68117214201111111548469/
0 0