【问题标题】:Fill TreeView with directories and files on drives用驱动器上的目录和文件填充 TreeView
【发布时间】:2015-01-16 00:05:03
【问题描述】:

我的 win32 api 项目中有一个 TreeView。 我想用驱动器上的目录和文件填充 TreeView。使用一个函数我得到所有可用的驱动器,然后我用驱动器号作为参数调用这个函数:

bool ListDirectoryContents(const char *sDir)
{
    WIN32_FIND_DATA fdFile;
    HANDLE hFind = 0;
    vector<string> FileNames;

    char sPath[2048];
    sprintf(sPath, "%s\\*.*", sDir);

    if((hFind = FindFirstFile(sPath, &fdFile)) == INVALID_HANDLE_VALUE)
    {
        printf("Path not found: [%s]\n", sDir);
        return false;
    }

    do
    {
        if(strcmp(fdFile.cFileName, ".") != 0 && strcmp(fdFile.cFileName, "..") != 0)
        {
            sprintf(sPath, "%s%s", sDir, fdFile.cFileName);
            if((fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            {
                if((fdFile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0)
                {
                    // Directories
                    AddItemToTree(hwndTree, sPath, 2);
                    //FileNames.insert(sPath);
                    //ListDirectoryContents(sPath); // Recursion
                }
            }
            else
            {
                if((fdFile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0)
                {
                    //Files
                    //AddItemToTree(hwndTree, sPath, 2);
                    FileNames.push_back(sPath);
                }
            }
        }
    }
    while(FindNextFile(hFind, &fdFile));

    FindClose(hFind);

    for(vector<string>::iterator FileName = FileNames.begin(); FileName != FileNames.end(); ++FileName)
    {
        AddItemToTree(hwndTree, (char*)FileName->c_str(), 2);
    }

    return true;
}

我用来首先列出目录和之后列出文件的 FileNames 向量。

AddItemToTree 函数:

HTREEITEM AddItemToTree(HWND hwndTree, char *text, int nLevel)
{
    TVINSERTSTRUCT tvins;
    static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;
    static HTREEITEM hRootItem = NULL;
    static HTREEITEM hPrevLev2Item = NULL;
    //tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_PARAM;
    tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIS_STATEIMAGEMASK;
    tvi.iImage = AddIconToTree(hwndTree, text);
    tvi.iSelectedImage = tvi.iImage;
    tvi.pszText = GetFileNameFromPath(text);
    tvins.hInsertAfter = hPrev;
    tvins.item = tvi;

    if(nLevel == 1)
    {
        tvins.hParent = TVI_ROOT;
    }
    else if(nLevel == 2)
    {
        tvins.hParent = hRootItem;
    }
    else
    {
        TVITEM tviSetup;
        tviSetup.hItem = hPrev;
        tviSetup.mask = TVIF_PARAM;
        TVITEM * tviLocal = &tviSetup;
        TreeView_GetItem(hwndTree, tviLocal);

        if(nLevel > tviLocal->lParam)
        {
            tvins.hParent = hPrev;
        }
        else
        {
            HTREEITEM hPrevLocal = TreeView_GetParent(hwndTree, hPrev);
            tviLocal->hItem = hPrevLocal;
            TreeView_GetItem(hwndTree, tviLocal);
            for(int i = nLevel; i <= tviLocal->lParam;)
            {
                HTREEITEM hPrevLocalTemp = TreeView_GetParent(hwndTree, hPrevLocal);
                hPrevLocal = hPrevLocalTemp;
                tviLocal->hItem = hPrevLocal;
                TreeView_GetItem(hwndTree, tviLocal);
            }
            tviLocal->mask = TVIF_TEXT;
            TreeView_GetItem(hwndTree, tviLocal);
            tvins.hParent = hPrevLocal;

        }
    }

    hPrev = (HTREEITEM)SendMessage(hwndTree, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);

    if(hPrev == 0)
    {
        return false;
    }
    if(nLevel == 1)
    {
        hRootItem = hPrev;
    }

    return hPrev;
}

问题是,如果我在 ListDirectoryContents 函数中使用递归调用,则需要很长时间才能填充 TreeView 中的所有目录和文件。

所以我认为它可以像这样工作:

  • 首先我要添加根目录(所有驱动器,例如 C:\、D:\...)
  • 我会找到驱动器中的所有子目录和文件,并将它们添加为驱动器根目录的子目录。
  • 然后如果我展开根节点,它会找到之前找到的驱动器子目录的所有子目录,依此类推...

但是问题来了,我怎样才能在某个地方插入一些项目?我的意思是AddItemToTree 函数中的第三个参数是什么?

有没有更简单的方法来用目录填充 TreeView? 提前谢谢大家!

【问题讨论】:

    标签: c++ winapi treeview directory subdirectory


    【解决方案1】:

    通常,您这样做的方式根本不是使用nLevel 参数,而是使用hItemParent 参数。

    也就是说,当您调用AddItemToTree 时,您将传递与父项对应的HTREEITEM 函数。当您插入新项目时,这将传递给 tvins.hParent 成员。

    因此,您当前拥有nLevel == 1,您将拥有AddItemToTree(hwndTree, "text", TVI_ROOT)

    要在子文件夹展开时填充它,您需要一个 WM_NOTIFY 处理程序来监视 TVN_ITEMEXPANDED 通知。

    当您收到此消息时,它会告诉您展开了哪个项目 - 这是您将传递给 AddItemToTree 的参数。

    case WM_NOTIFY:
        if (((LPNMHDR)lParam)->code == TVN_ITEMEXPANDED)
        {
            // work out the path to read, and then ...
            AddItemToTree(hwndTree, <path>, ((LPNMTREEVIEW)lParam)->itemNew.hItem);
        }
    

    【讨论】:

    • 还有一个TVN_ITEMEXPANDING通知,在节点展开前触发,而TVN_ITEMEXPANDED在节点展开后触发。例如,TVN_ITEMEXPANDING 可以取消扩展,但 TVN_ITEMEXPANDED 不能。根据您应用的特定需求,TVN_ITEMEXPANDING 可能是填充子节点的更好选择。
    • 那么当我想成为 TVI_ROOT 的子节点时应该传递什么参数呢?我正在尝试通过 TVI_LAST,但这会给我一个异常错误...我知道为什么,但不知道如何正确地做到这一点..
    • @ProXicT:要生成TVI_ROOT 的孩子,您需要传递TVI_ROOT。不要混淆指定 parent 项的 hParent 成员和指定相对于同级项的定位的 hInsertAfter 成员(TVI_LAST 用于定位,而不是指定父)。
    • 好吧,我现在真的很困惑,当我想在树视图中创建一个 ROOT 时,我将 TVI_ROOT 作为参数传递,如下所示:HTREEITEM AddItemToTree(HWND hwndTree, char *text, HTREEITEM parent) { ... ... tvins.hInsertAfter = hPrev; tvins.item = tvi; tvins.hParent = parent; hPrev = SendM... 那么我应该如何使用相同的参数?
    • 树的根是尚未添加任何内容之前的树。 TVI_ROOT 不是一个实际的项目,它只是意味着“在根级别添加这个项目”。其他所有内容都是实际项目的子项。
    猜你喜欢
    • 2011-09-08
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 2021-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多