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

VC 技巧:列表控制項實現資料夾縮圖顯示 (虛擬清單 + 自繪)

     編輯:VC++

技術棧

  • 虛擬清單模式 (LVS_OWNERDATA):按需獲取顯示資訊,支援大量檔案不卡頓。
  • 自訂繪製 (NM_CUSTOMDRAW):在圖示區域繪製縮圖,替換系統預設圖示。
  • Shell 介面:IShellFolder、IShellIcon、IShellIconOverlay、ILCombine、OleLoadPicturePath。

核心實作步驟

1. 建立虛擬清單視圖

BOOL CThumbView::PreCreateWindow(CREATESTRUCT& cs)
{
    cs.style &= ~LVS_TYPEMASK;
    cs.style |= LVS_ICON | LVS_OWNERDATA; // 圖示模式 + 虛擬模式
    return CListView::PreCreateWindow(cs);
}

void CThumbView::OnInitialUpdate()
{
    CListView::OnInitialUpdate();
    GetListCtrl().SetCallbackMask(LVIS_OVERLAYMASK); // 動態 Overlay 圖示
}

2. 緩存顯示資訊 (LVN_ODCACHEHINT / LVN_GETDISPINFO)

// 預載入可視範圍前後各 5 項
void CThumbView::OnOdcachehint(NMHDR* pNMHDR, LRESULT* pResult)
{
    NMLVCACHEHINT* pHint = (NMLVCACHEHINT*)pNMHDR;
    PrepCache(0, min(5, m_items.GetSize()));
    PrepCache(pHint->iFrom, pHint->iTo);
    PrepCache(max(0, m_items.GetSize()-5), m_items.GetSize());
    *pResult = 0;
}

// 提供單項顯示資訊 (文字、圖示、Overlay)
void CThumbView::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult)
{
    LV_DISPINFO* pInfo = (LV_DISPINFO*)pNMHDR;
    if (pInfo->item.iItem == -1) return;

    LPITEMIDLIST pidl = m_items[pInfo->item.iItem];
    CFolderItemInfo* pCache = FindInCache(pidl);
    bool bCached = true;
    if (!pCache) { pCache = new CFolderItemInfo; GetItemInfo(pidl, pCache); bCached = false; }

    if (pInfo->item.mask & LVIF_TEXT)
        lstrcpyn(pInfo->item.pszText, pCache->szDisplayName, pInfo->item.cchTextMax);
    if (pInfo->item.mask & LVIF_IMAGE)
        pInfo->item.iImage = pCache->iIcon;
    if (pInfo->item.mask & LVIF_STATE)
        pInfo->item.state = pCache->state; // Overlay 狀態

    if (!bCached) delete pCache;
    *pResult = 0;
}

3. 取得檔案完整資訊 (系統圖示、Overlay、縮圖)


void CThumbView::GetItemInfo(LPCITEMIDLIST pidl, CFolderItemInfo* pInfo)
{
    // 1. 顯示名稱
    theApp.SHGetDisplayNameOf(pidl, pInfo->szDisplayName);

    // 2. 系統圖示索引
    IShellIcon* pIcon = NULL;
    if (SUCCEEDED(m_pFolder->QueryInterface(IID_IShellIcon, (void**)&pIcon)))
    {
        pIcon->GetIconOf(pidl, 0, &pInfo->iIcon);
        pIcon->Release();
    }

    // 3. Overlay 圖示 (捷徑箭頭、共享手掌等)
    IShellIconOverlay* pOverlay = NULL;
    if (SUCCEEDED(m_pFolder->QueryInterface(IID_IShellIconOverlay, (void**)&pOverlay)))
    {
        int nOverlay = 0;
        pOverlay->GetOverlayIndex(pidl, &nOverlay);
        pInfo->state = INDEXTOOVERLAYMASK(nOverlay);
        pOverlay->Release();
    }

    // 4. 縮圖載入 (僅圖片檔)
    LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl);
    if (pidlFull && SHGetPathFromIDList(pidlFull, pInfo->szPath))
    {
        HRESULT hr = OleLoadPicturePath(T2OLE(pInfo->szPath), NULL, 0, RGB(255,255,255),
                                        IID_IPicture, (void**)&pInfo->pic.m_pPict);
        if (FAILED(hr)) pInfo->bFailLoadPic = TRUE;
    }
    m_pMalloc->Free(pidlFull);
}

4. 自繪縮圖 (NM_CUSTOMDRAW - CDDS_ITEMPOSTPAINT)


void CThumbView::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
    LPNMLVCUSTOMDRAW pDraw = (LPNMLVCUSTOMDRAW)pNMHDR;
    switch (pDraw->nmcd.dwDrawStage)
    {
    case CDDS_PREPAINT: *pResult = CDRF_NOTIFYITEMDRAW; break;
    case CDDS_ITEMPREPAINT: *pResult = CDRF_NOTIFYPOSTPAINT; break;
    case CDDS_ITEMPOSTPAINT:
        {
            int iItem = (int)pDraw->nmcd.dwItemSpec;
            CFolderItemInfo* pInfo = FindInCache(m_items[iItem]);
            if (!pInfo || pInfo->bFailLoadPic || !pInfo->pic.m_pPict)
            { *pResult = CDRF_DODEFAULT; break; }

            CRect rcIcon; GetListCtrl().GetItemRect(iItem, &rcIcon, LVIR_ICON);
            CDC* pDC = CDC::FromHandle(pDraw->nmcd.hdc);
            pInfo->pic.Render(pDC, rcIcon, rcIcon); // IPicture::Render 繪製
            *pResult = CDRF_NEWFONT;
        }
        break;
    }
    *pResult = 0;
}

5. 目錄切換與 PIDL 管理


void CThumbView::EnterFolder(LPCITEMIDLIST pidl)
{
    m_pidlFolder = ILClone(pidl);
    m_pFolder->Release(); // 釋放舊介面
    m_items.RemoveAll(); m_mapCache.RemoveAll();

    m_pDesktop->BindToObject(m_pidlFolder, NULL, IID_IShellFolder, (void**)&m_pFolder);
    IEnumIDList* pEnum; m_pFolder->EnumObjects(NULL, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS, &pEnum);
    LPITEMIDLIST pidlItem; ULONG fetched;
    while (pEnum->Next(1, &pidlItem, &fetched) == S_OK && fetched == 1)
        m_items.Add(pidlItem);
    pEnum->Release();

    GetListCtrl().SetItemCount(m_items.GetSize());
}

效能優化建議

  • 自訂圖示尺寸,完全自繪項目避免拉伸開銷。
  • 背景執行緒非同步載入圖片,載入完成發送 RedrawItems。
  • 預先產生符合圖示尺寸的縮圖快取,繪製時直接 BitBlt。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved