フォルダをツリーコントロールに一覧する

test114.gif
ここでは特定のフォルダ内に存在するサブフォルダを一覧する簡易的なツリーコントロールクラスを作成した。

このクラスはかなり制限が多く、実装に癖があるので"実用"する場合はゼロから作り直した方が早いだろう。
トップレベルのフォルダは1つのみで、シェルフォルダやアイコンの表示機能などはない。
またフォルダがサブフォルダを持つ場合に、ツリーコントロール上で「+」マークが表示されるように、ツリーを開く際にその都度、孫フォルダまで取得している。これは本来スレッドを立ててマルチタスクに読み込むべき処理だが、そのまま簡単に読み込んでいる。



■ATL/WTLアプリケーションウイザードでダイアログベースのプロジェクトを作成する。

■リソースエディタでメインダイアログの大きさを適当に大きくする。

■MainDlg.hの先頭にクラス定義などを追加

#include "atlstr.h"
#include "atlcoll.h"


class	CDnpFolderTreeCtrl	: public CTreeViewCtrl
{
protected:
	CAtlString	_strRootName;
	CAtlString	_strRootPath;
public:

	BEGIN_MSG_MAP(CDnpFolderTreeCtrl)
		NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDING,OnItemExpanding)
	END_MSG_MAP()
 

	bool	Initialize(LPCTSTR pszRootName,LPCTSTR pszRootPath)
	{
		bool		ret;
		HTREEITEM	hRoot;
		CAtlString	strPath;
		CAtlArray<CAtlString>	astrFolder;

		if(pszRootName == NULL || pszRootPath == NULL)
			return	false;
		if(*pszRootName == NULL || *pszRootPath == NULL)
			return	false;

		DeleteAllItems();

		strPath = pszRootPath;
		if(strPath.Right(1) != _T("\\") && strPath.Right(1) != _T("/"))
			strPath += _T("\\");

		_strRootName = pszRootName;
		_strRootPath = strPath;

		hRoot = InsertItem(pszRootName,TVI_ROOT,TVI_LAST);
		if(hRoot == NULL)
			return	false;

		ret = GetFolderList(_strRootPath,astrFolder);
		if(ret)
			ret = AddTreeFolder(hRoot,astrFolder,_strRootPath,0);
		if(ret == false)
			return	false;

		Expand(hRoot);

		return	ret;
	}


	//
	//	ツリーアイテムの示すフォルダへのパス取得
	//
	bool	GetPath(HTREEITEM hItem,CAtlString& strPath)
	{
		BOOL	ret;
		TCHAR	pszBuff[MAX_PATH];
		CAtlString	strBuff;

		//本来ならばITEMDATAを利用してフォルダパスを保存しておくべきだが
		//ここではツリーに登録したツリーアイテム名からパスを復元している
		strPath = _T("");
		while(1)
		{
			ret = GetItemText(hItem,pszBuff,MAX_PATH);
			if(ret == FALSE)
				return	false;
			strBuff = pszBuff;
			strBuff += _T("\\");
			strPath = strBuff + strPath;

			hItem = GetParentItem(hItem);
			if(hItem == NULL)
				break;
		}

		//先頭がルート名になっているはず
		if(strPath.Find(_strRootName) != 0)
		{
			strPath = _T("");
			return	false;
		}

		//先頭のルート名部分をルートパスへ置き換える
		strPath = strPath.Right(strPath.GetLength() - _strRootName.GetLength() - 1);
		strPath = _strRootPath + strPath;

		return	true;
	}



protected:

	//
	//ツリーアイテムを開いているときの処理
	//
	//孫アイテムが登録されていない場合は孫を登録する
	//
	LRESULT OnItemExpanding(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
	{
		LPNMTREEVIEW	pNmTreeView = (LPNMTREEVIEW)pnmh;

		if(pNmTreeView == NULL)
			return	0;

		//ツリーが閉じるだけなら処理はなし
		if(pNmTreeView->action == TVE_COLLAPSE)
			return	0;


		bool		ret;
		HTREEITEM	hItem;
		CAtlString	strPath;

		//開かれようとしているフォルダのパスを取得
		hItem = pNmTreeView->itemNew.hItem;
		ret = GetPath(hItem,strPath);
		if(ret == false)
			return	0;


		//孫フォルダー情報までツリーに登録されているかどうか
		if(IsLoadedFolder(hItem))
			return	0;


		BOOL		bRet;
		HTREEITEM	hChildItem;
		TCHAR		pszBuff[MAX_PATH];

		//これから開くツリーの子を列挙。孫フォルダーがツリーに内場合は追加
		hChildItem = GetChildItem(pNmTreeView->itemNew.hItem);
		while(GetParentItem(hChildItem) == hItem)
		{
			if(GetChildItem(hChildItem))
				break;

			bRet = GetItemText(hChildItem,pszBuff,MAX_PATH);
			if(bRet == FALSE)
				return	0;

			//フォルダ情報をツリーに追加
			{
				CAtlString	strBuff;
				CAtlArray<CAtlString>	astrChildFolder;

				//孫フォルダーリスト取得
				strBuff = strPath + pszBuff;
				ret = GetFolderList(strBuff,astrChildFolder);

				//ツリーに追加
				if(ret)
					AddTreeFolder(hChildItem,astrChildFolder);
			}

			hChildItem = GetNextItem(hChildItem,TVGN_NEXTVISIBLE);
			if(hChildItem == NULL)
				break;
		}

		return	0;
	}








	//
	//	hParentの子フォルダーはその子フォルダー情報を持っているかどうか
	//
	bool	IsLoadedFolder(HTREEITEM hParent)
	{
		HTREEITEM	hChildItem;

		hChildItem = GetChildItem(hParent);
		while(GetParentItem(hChildItem) == hParent)
		{
			if(GetChildItem(hChildItem))
				return	true;

			hChildItem = GetNextItem(hChildItem,TVGN_NEXTVISIBLE);
			if(hChildItem == NULL)
				break;
		}

		return	false;
	}









	bool	AddTreeFolder(HTREEITEM hParent,const CAtlArray<CAtlString>& astrFolderName,LPCTSTR pszPath=NULL,int nAddChild=0)
	{
		size_t		i;
		size_t		nSize;
		HTREEITEM	hChild;

		if(pszPath == NULL && nAddChild)
			return	false;		//pszPath==NULLのときはnAddChild==0でないとダメ

		nSize = astrFolderName.GetCount();
		for(i = 0; i < nSize; i++)
		{
			hChild = InsertItem(astrFolderName[i],hParent,TVI_LAST);
			if(hChild == NULL)
				continue;

			if(nAddChild == 0)
				continue;

			bool		ret;
			CAtlString	strPath;
			CAtlArray<CAtlString>	astrChildFolder;

			strPath = pszPath;
			strPath += astrFolderName[i];
			strPath += _T("\\");
			ret = GetFolderList(strPath,astrChildFolder);
			if(ret == false)
				continue;

			AddTreeFolder(hChild,astrChildFolder,strPath,nAddChild - 1);
		}

		return	true;
	}


	//
	//	ソート用仮想関数
	//
	//派生クラスでastrFolderName[nFirstIndex]からnCountの数だけ任意の方法で並べ替える
	//
	virtual	bool	SortData(CAtlArray<CAtlString>& astrFolderName,size_t nFirstIndex,size_t nCount)
	{
		return	true;
	}


	//
	//	フォルダの列挙
	//
	//指定したフォルダ内のフォルダを列挙します。
	//	pszPath内のフォルダを列挙してastrFolderName配列へ追加する
	//
	bool	GetFolderList(LPCTSTR pszPath,CAtlArray<CAtlString>& astrFolderName)
	{
		BOOL			bFind;
		HANDLE			hFind;
		WIN32_FIND_DATA	FindFileData;
		CAtlString		strFolder;

		if(pszPath == NULL || _tcslen(pszPath) == 0)
			return	false;

		strFolder = pszPath;
		if(strFolder.Right(1) != _T('\\') && strFolder.Right(1) != _T('/'))
			strFolder += _T("\\");

		hFind = ::FindFirstFile(strFolder + _T("*.*"),&FindFileData);
		if(hFind == INVALID_HANDLE_VALUE)
			return	false;

		size_t	nBefore;
		size_t	nAfter;

		nBefore = astrFolderName.GetCount();

		bFind = TRUE;
		while(bFind)
		{
			if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				//リンクフォルダや隠しフォルダは表示しない
				if((FindFileData.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_HIDDEN)) == 0)
					if(_tcsncmp(FindFileData.cFileName,_T("."),1) != 0 && _tcsncmp(FindFileData.cFileName,_T(".."),2) != 0 )
						astrFolderName.Add(FindFileData.cFileName);
			}

			bFind = ::FindNextFile(hFind,&FindFileData);
		}
		::FindClose(hFind);

		nAfter = astrFolderName.GetCount();

		if(nAfter == nBefore)
			return	true;

		return	SortData(astrFolderName,nBefore + 1,nAfter - nBefore);
	}
};
■MainDlg.hのCMainDlgにメンバー変数を追加
	CDnpFolderTreeCtrl	_wndTree;
■MainDlg.hのCMainDlgのメッセージマップとOnInitDialogに追加
	BEGIN_MSG_MAP(CMainDlg)
(。。。省略。。。)
		CHAIN_MSG_MAP_MEMBER(_wndTree)
	END_MSG_MAP()

	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{

(。。。省略。。。)
		RECT	rect;		

		GetClientRect(&rect);
		rect.top += 10;
		rect.bottom -= 10;
		rect.left += 10;
		rect.right -=100;

		_wndTree.Create(m_hWnd,rect,NULL,WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TVS_HASBUTTONS | TVS_HASLINES,WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT); 
		_wndTree.Initialize(_T("cドライブ"),_T("c:\\"));

		return TRUE;
	}


プロジェクトファイルをダウンロード


カテゴリー「コントロール」 のエントリー