エクスプローラ風のツリービューを作成するには?

解決


立川  2002-11-12 09:30:03  No: 50592  IP: [192.*.*.*]

初めまして立川といいます。

早速なのですが、
現在MFCのダイアログベースでエクスプローラのようなものを作っています。
超初歩的な質問で恐縮なのですが、ツリーへのアイテムの追加方法がわかりません。
エクスプローラ形式ということで、デスクトップがトップにあるツリーを作りたいです。
SHGetDesktopFolderというのを使うとデスクトップを使えるってのはわかったのですが、
如何せんツリーを根本的に理解できていないもので…。
CTreeCtrl::InsertItemを使うというのはわかるのですが…。
どうぞご教授くださいませ!
vc++ 6.0 を使用しております。

編集 削除
YuO  2002-11-12 23:48:06  No: 50593  IP: [192.*.*.*]

CTreeCtrl::InsertItemについて,どの程度理解できましたか?

例えば,
CTreeCtrl TreeCtrl;
というのがあったとして,
for (int i = 0; i < 10; ++i) {
    char buf[10];
    sprintf(buf, "%u", i);
    HTREEITEM hParent = TreeCtrl.InsertItem(buf);
    for (int j = 0; j < 10; ++j) {
        sprintf(buf, "%u", i * j);
        TreeCtrl.InsertItem(buf, hParent);
    }
}
というコードを見て,どのようなツリーが生成されるか想像できますか?
とりあえず,
HTREEITEM CTreeCtrl::InsertItem (LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST);
の簡単な使い方になります。

編集 削除
グランディス  2002-11-13 00:12:54  No: 50594  IP: [192.*.*.*]

こんな感じです。
引数の詳細はヘルプなどを見てください。

CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_?????);
// 2,3番目の引数はイメージリストに登録されたイメージのインデックス
HTREEITEM hItemRoot = tree -> InsertItem("データ", 0, 1);

編集 削除
グランディス  2002-11-13 00:18:25  No: 50595  IP: [192.*.*.*]

YuOさんがすでに書かれていたのに気づきませんでした。
私のは無視してください。

編集 削除
立川  2002-11-13 10:35:29  No: 50596  IP: [192.*.*.*]

YuO様ありがとうございました!
iの下にi*jの結果がぶら下がるツリーになると思います。
2の下に0,2,4,6,8,…
3の下に0,3,6,9,12,…
といった感じでしょうか?この答えが合っているとしたら結構わかりやすかったです。

グランディス様ありがとうございました!
こちらの方もパッと見、後々必ず必要になりそうなので無視できませんw

編集 削除
YuO  2002-11-13 12:42:34  No: 50597  IP: [192.*.*.*]

> この答えが合っているとしたら結構わかりやすかったです。

その通りです。
グランディスさんが書かれたのは,
HTREEITEM CTreeCtrl::InsertItem(LPCTSTR lpszItem,
                                int nImage,
                                int nSelectedImage,
                                HTREEITEM hParent = TVI_ROOT,
                                HTREEITEM hInsertAfter = TVI_LAST);
の使い方ですね。
最終的にはこちらを使うことになると思いますが,
HTREEITEM CTreeCtrl::InsertItem(LPCTSTR lpszItem,
                                HTREEITEM hParent = TVI_ROOT,
                                HTREEITEM hInsertAfter = TVI_LAST);
の使い方がわかっていれば,後はイメージリスト関係の話を覚えるだけです。


で,次はデスクトップからのツリーの取得ですか……。これは非常にやっかいですなぁ……。
表示名はIShellFolder::GetDisplayNameOfで取得するとして,子フォルダは……。
IShellFolder::EnumObjectsでIEnumIDListを取得して,
IEnumIDList::Nextで列挙したITEMIDLISTを元にIShellFolder::BindToObjectで子のIShellFolderを取得,
の繰り返しでできるかと。
#とりあえず,ヘルプのみで書いています。IShellFolderなんて使ったことないので。

編集 削除
立川  2002-11-13 13:43:36  No: 50598  IP: [192.*.*.*]

YuO様ありがとうございます。
合っていたようでとりあえずホッとしましたw

>デスクトップからのツリーの取得ですか……。これは非常にやっかいですなぁ……。
MSDN見ても日本語でないしw、何が引数になっているのか理解するのに結構時間かかりそうです。
でも、YuO様のおかげで流れがわかったので、かなりの時間短縮になると思います。
また下手なりにがんばってみたいと思います。
P.S.つくづく「エクスプローラって実はすごいんだ…」と感じさせられました…w

編集 削除
立川  2002-11-14 15:30:20  No: 50599  IP: [192.*.*.*]

立川です。
先日YuO様に教えていただき、
それをもとに作っていたのですが、どうしても前へ進めなくなってしまいました。
原因がわからないのでどうかご助言お願いします。
LPSHELLFOLDER  lpShellFolder;
LPITEMIDLIST  pFileIDList;
LPENUMIDLIST  pEnumIDList;
LPSHELLFOLDER  pCurFolder;
STRRET    stFileName;
ULONG    ulRetNo;
CString    strFileName;
OLECHAR    ochPath[MAX_PATH];

SHGetDesktopFolder(&lpShellFolder);
lpShellFolder->EnumObjects(GetSafeHwnd(),
                           SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
                           &pEnumIDList);
while(pEnumIDList->Next(1, &pFileIDList, &ulRetNo) != S_FALSE){
    hRes = lpShellFolder->BindToObject(pFileIDList, NULL,
                                       IID_IShellFolder,
                                       (LPVOID *)&pCurFolder);
    pCurFolder->GetDisplayNameOf(pFileIDList, SHGDN_NORMAL,
                                 &stFileName);
    strFileName = TFileName(pFileIDList, &stFileName);
    //
    //CTreeCtrl::InsertItemする所
    //
}
TFileNameという関数は中でMultiByteToWideChar()とかをやって文字列を表示
させる準備をしています。戻り値はCString型です。
これのBindToObjectがNOERRORを返してくるので、どうやら失敗しているようです。
BindToObjectの前でいけない事してると思うのですが、
何がいけないのか正直わかりません。
汚いソースですが宜しくお願いします

編集 削除
立川  2002-11-14 15:45:48  No: 50600  IP: [192.*.*.*]

訂正です。
> BindToObjectがNOERRORを返してくるので、どうやら失敗しているようです。
その前のwhileでS_FALSEが返って来たらWhile抜けるとしていますが、
どうも抜けていないようです。
その為かアプリケーションエラーで怒られます。
どこらへんがいけないのでしょうか?
ご教授お願いします。

編集 削除
YuO  URL  2002-11-15 00:28:02  No: 50601  IP: [192.*.*.*]

> これのBindToObjectがNOERRORを返してくるので、どうやら失敗しているようです。
NOERRORは成功時ですが……。

とりあえず,
> while(pEnumIDList->Next(1, &pFileIDList, &ulRetNo) != S_FALSE){
この行は,
while (pEnumIDList->Next(1, &pFileIDList, &ulRetNo) == NOERROR) {
に置き換えてみてください。
私も最初SUCCEEDEDマクロでチェックしてはまりました。

一応,私が作ったデスクトップ以下の列挙テストプログラムを
[HomePage]のところにおいておきます。汚いソースですが……。
#makefile.vcがVC++付属nmake用,makefile.bccがBCC付属make用のMakefileです。

編集 削除
立川  2002-11-15 11:02:40  No: 50602  IP: [192.*.*.*]

YuO様ありがとうございます!
>while (pEnumIDList->Next(1, &pFileIDList, &ulRetNo) == NOERROR) {
>に置き換えてみてください。
教えられたとおりやってみました。
BindToObjectの部分ではエラーは起こらなくなったのですが、
その次のGetDisplayNameOfでエラーが起きてしまいました。
BindToObjectの第4引数があるとき(私の場合3回目に通った時)、0x00000000を返してきているために起きているようです。
…相変わらずさっぱりです。

わざわざプログラムソースありがとうございました!
スマートに書かれていて、読みやすかったです。
でも、低級な私には少々難解な記述でしたので、完全理解にはまだ時間がかかりそうです(^-^;
現在誠意読解中です

編集 削除
立川  2002-11-15 11:16:32  No: 50603  IP: [192.*.*.*]

情報追加です。
エラーが起きたGetDisplayNameOfなのですが、戻り値を取得して以下の文を追加しました。
if( hRes != NOERROR ){
    AfxMessageBox( "失敗" );
    return TRUE;
}
実行すると初っ端からメッセージボックスが出されてしまいました。
とすると根本から間違っているのでしょうか…

編集 削除
立川  2002-11-20 09:26:09  No: 50604  IP: [192.*.*.*]

何とかなりました。お手数おかけしましてすみませんでした!

編集 削除