![]()
ここでは特定のフォルダ内に存在するサブフォルダを一覧する簡易的なツリーコントロールクラスを作成した。
このクラスはかなり制限が多く、実装に癖があるので"実用"する場合はゼロから作り直した方が早いだろう。
トップレベルのフォルダは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;
}
