用驱动器上的目录和文件填充树视图

Fill TreeView with directories and files on drives

本文关键字:文件 填充 视图 驱动器      更新时间:2023-10-16

我的win32 api项目中有一个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;
}

文件名的向量我用来先列出目录,然后再列出文件。

函数 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函数中的第三个参数是什么?

有没有更简单的方法可以用目录填充树视图?提前谢谢大家!

通常,执行此操作的方式根本不是使用nLevel参数,而是使用hItemParent参数。

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

所以你目前有nLevel == 1的地方,你会有AddItemToTree(hwndTree, "text", TVI_ROOT).

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

当您收到此消息时,它会告诉您展开了哪个项目 - 这是您要传递给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);
    }