首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ TreeCtrl 與 ListCtrl 聯動實作資源管理器風格檔案瀏覽器

     編輯:VC 教學

前言

經典的「左側樹狀目錄 + 右側檔案清單」介面是檔案瀏覽器的標準範式。本文示範在 VC++ 對話方塊專案中,利用 `CTreeCtrl`、`CListCtrl`、`CImageList`、`SHGetFileInfo` 系統 API,實作邏輯磁碟機枚舉、目錄懶加載展開、檔案圖示提取與顯示、雙擊開啟檔案完整功能,代碼精簡、無第三方依賴。

一、介面佈局與風格設定

  1. 對話方塊放置 `TreeCtrl` (IDC_TREE) 與 `ListCtrl` (IDC_LIST),ListCtrl 設為 `LVS_REPORT` + `LVS_SHAREIMAGELISTS`。
  2. TreeCtrl 風格加入:`TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT`。

BOOL CFileBrowserDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    // Tree 風格
    DWORD dwStyle = GetWindowLong(m_tree.m_hWnd, GWL_STYLE);
    dwStyle |= TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT;
    SetWindowLong(m_tree.m_hWnd, GWL_STYLE, dwStyle);

    // 影像清單共用
    m_imgList.Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 10);
    m_tree.SetImageList(&m_imgList, TVSIL_NORMAL);
    m_list.SetImageList(&m_imgList, LVSIL_SMALL);

    // 根節點
    m_hRoot = m_tree.InsertItem(_T("我的電腦"), 0, 0);
    EnumLogicalDrives(m_hRoot);
    m_tree.Expand(m_hRoot, TVE_EXPAND);
    return TRUE;
}

二、枚舉邏輯磁碟機


void CFileBrowserDlg::EnumLogicalDrives(HTREEITEM hParent)
{
    TCHAR szDrives[256];
    DWORD dwLen = GetLogicalDriveStrings(256, szDrives);
    TCHAR* p = szDrives;
    while (*p)
    {
        // 取得磁碟機圖示
        SHFILEINFO sfi;
        SHGetFileInfo(p, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SMALLICON);
        int nImg = m_imgList.Add(sfi.hIcon);
        DestroyIcon(sfi.hIcon);

        HTREEITEM hDrive = m_tree.InsertItem(p, nImg, nImg, hParent);
        // 插入佔位子節點,讓節點顯示 '+' 號
        m_tree.InsertItem(_T(""), hDrive);
        p += _tcslen(p) + 1;
    }
}

三、目錄懶加載:TVN_ITEMEXPANDING


void CFileBrowserDlg::OnItemExpandingTree(NMHDR* pNMHDR, LRESULT* pResult)
{
    NM_TREEVIEW* pNM = (NM_TREEVIEW*)pNMHDR;
    HTREEITEM hItem = pNM->itemNew.hItem;
    if (hItem == m_hRoot) { *pResult = 0; return; }

    // 移除佔位子節點
    HTREEITEM hChild = m_tree.GetChildItem(hItem);
    while (hChild)
    {
        HTREEITEM hNext = m_tree.GetNextItem(hChild, TVGN_NEXT);
        if (GetItemText(hChild).IsEmpty()) m_tree.DeleteItem(hChild);
        hChild = hNext;
    }

    // 取得完整路徑
    CString strPath = GetFullPath(hItem);
    if (strPath.Right(1) != _T("\\")) strPath += _T("\\");

    // 枚舉子目錄
    CFileFind finder;
    BOOL bFound = finder.FindFile(strPath + _T("*.*"));
    while (bFound)
    {
        bFound = finder.FindNextFile();
        if (finder.IsDirectory() && !finder.IsDots())
        {
            SHFILEINFO sfi;
            SHGetFileInfo(finder.GetFilePath(), 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SMALLICON);
            int nImg = m_imgList.Add(sfi.hIcon);
            DestroyIcon(sfi.hIcon);
            HTREEITEM hSub = m_tree.InsertItem(finder.GetFileName(), nImg, nImg, hItem);
            m_tree.InsertItem(_T(""), hSub); // 佔位
        }
    }
    *pResult = 0;
}

CString CFileBrowserDlg::GetFullPath(HTREEITEM hItem)
{
    CString strPath, strItem;
    while (hItem && hItem != m_hRoot)
    {
        strItem = m_tree.GetItemText(hItem);
        strPath = strItem + _T("\\") + strPath;
        hItem = m_tree.GetParentItem(hItem);
    }
    return strPath;
}

四、選擇目錄顯示檔案:TVN_SELCHANGED


void CFileBrowserDlg::OnSelChangedTree(NMHDR* pNMHDR, LRESULT* pResult)
{
    NM_TREEVIEW* pNM = (NM_TREEVIEW*)pNMHDR;
    HTREEITEM hItem = pNM->itemNew.hItem;
    if (hItem == m_hRoot) { *pResult = 0; return; }

    m_list.DeleteAllItems();
    CString strPath = GetFullPath(hItem);
    if (strPath.Right(1) != _T("\\")) strPath += _T("\\");

    CFileFind finder;
    BOOL bFound = finder.FindFile(strPath + _T("*.*"));
    int nItem = 0;
    while (bFound)
    {
        bFound = finder.FindNextFile();
        if (finder.IsDots()) continue;
        if (finder.IsDirectory()) continue; // 僅顯示檔案,目錄由左側樹狀管理

        SHFILEINFO sfi;
        SHGetFileInfo(finder.GetFilePath(), 0, &sfi, sizeof(sfi), 
                      SHGFI_ICON | SHGFI_SMALLICON | SHGFI_DISPLAYNAME);
        int nImg = m_imgList.Add(sfi.hIcon);
        DestroyIcon(sfi.hIcon);

        m_list.InsertItem(nItem++, sfi.szDisplayName, nImg);
        // 可加子項:大小、修改時間、屬性
        // m_list.SetItemText(nItem-1, 1, ...);
    }
    *pResult = 0;
}

五、雙擊開啟檔案:NM_DBLCLK


void CFileBrowserDlg::OnDblClkList(NMHDR* pNMHDR, LRESULT* pResult)
{
    LPNMITEMACTIVATE pNM = (LPNMITEMACTIVATE)pNMHDR;
    if (pNM->iItem < 0) return;

    CString strName = m_list.GetItemText(pNM->iItem, 0);
    HTREEITEM hSel = m_tree.GetSelectedItem();
    CString strPath = GetFullPath(hSel);
    if (strPath.Right(1) != _T("\\")) strPath += _T("\\");
    strPath += strName;

    // 使用 ShellExecute 開啟(關聯應用程式)
    HINSTANCE h = ShellExecute(m_hWnd, _T("open"), strPath, NULL, NULL, SW_SHOWNORMAL);
    if ((INT_PTR)h <= 32) AfxMessageBox(_T("無法開啟檔案"));
    *pResult = 0;
}

六、資源清理


CFileBrowserDlg::~CFileBrowserDlg()
{
    // 影像清單圖示由 ImageList 管理,控制項銷毀時自動釋放
    // 若需手動釋放:m_imgList.DeleteImageList();
}

七、執行效果

資源管理器風格瀏覽器示範

左側樹狀顯示磁碟機與目錄結構,展開時動態載入子目錄;右側列表顯示當前目錄檔案名稱與系統圖示,雙擊以關聯程式開啟。

八、擴充建議

  • 多執行緒枚舉:大目錄枚舉會卡死 UI,可用 `AfxBeginThread` 背景掃描,完成後 `PostMessage` 更新樹狀。
  • 虛擬清單:`LVS_OWNERDATA` 模式支援百萬級檔案,按需查詢。
  • 檔案系統監視:`ReadDirectoryChangesW` 即時反映建立/刪除/重命名。
  • 縮圖模式:`LVS_ICON` + `SHGetFileInfo` 取大圖示 (`SHGFI_LARGEICON`)。

九、結語

本範例以不到 200 行核心代碼實現功能完整的檔案瀏覽器雛形,展示了 `CTreeCtrl` 懶加載、`CListCtrl` 圖示共享、`SHGetFileInfo` 系統圖示提取、`CFileFind` 枚舉、`ShellExecute` 開啟檔案等 Windows Shell 程式設計核心技術。架構清晰,易於擴充為專業級檔案管理器、專案檔案視圖、FTP 瀏覽器等應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved