CMFCShellTreeCtrl控件的效率改进

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/
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值