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

虛擬清單視圖:在 CListView 中高效顯示海量資料庫記錄

     編輯:關於VC++

痛點與解決方案

傳統 CListView 需預先插入所有項目,資料量大時極其緩慢。IE4 引入的 虛擬清單模式 (LVS_OWNERDATA) 僅在需要顯示時透過 LVN_GETDISPINFO 回呼請求資料,極大提升效能。本文示範搭配 DAO 動態集記錄集實現。

關鍵實現步驟

1. 開啟資料庫與動態集記錄集

BOOL CVirtualListDoc::OnOpenDocument(LPCTSTR lpszPathname) {
    m_strFile = lpszPathname;
    m_pDB = new CDaoDatabase; m_pDB->Open(m_strFile);
    m_pRS = new CDaoRecordset(m_pDB);
    m_pRS->Open(dbOpenDynaset, _T("SELECT * FROM TableName ORDER BY Col"));
    m_pRS->MoveLast(); // 強制填充記錄集以取得正確 RecordCount
    return TRUE;
}

關鍵:必須使用 dbOpenDynaset 才支援 SetAbsolutePosition 隨機存取。

2. 設定虛擬清單風格與欄位

BOOL CVirtualListView::PreCreateWindow(CREATESTRUCT& cs) {
    cs.lpszClass = WC_LISTVIEW;
    cs.style &= ~LVS_TYPEMASK;
    cs.style |= LVS_REPORT | LVS_EDITLABELS | LVS_OWNERDATA; // 關鍵風格
    return CListView::PreCreateWindow(cs);
}

void CVirtualListView::OnInitialUpdate() {
    CListView::OnInitialUpdate();
    CListCtrl& list = GetListCtrl();

    // 擴充風格:全列選取、格線、標題拖曳追蹤
    DWORD ex = LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP | LVS_EX_TRACKSELECT;
    list.SendMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, ex);

    // 動態建立欄位
    LV_COLUMN lvc = { LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_LEFT, 120 };
    for (int i = 0; i < m_pRS->GetFieldCount(); ++i) {
        CDaoFieldInfo fi; m_pRS->GetFieldInfo(i, fi);
        lvc.pszText = (LPTSTR)(LPCTSTR)fi.m_strName;
        list.InsertColumn(i, &lvc);
    }

    // 設定虛擬項目總數
    m_nCount = m_pRS->GetRecordCount();
    list.SendMessage(LVM_SETITEMCOUNT, m_nCount, LVSICF_NOINVALIDATEALL);
}

3. 處理 LVN_GETDISPINFO 即時提供資料

void CVirtualListView::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult) {
    LV_DISPINFO* pDisp = (LV_DISPINFO*)pNMHDR;
    if (!(pDisp->item.mask & LVIF_TEXT)) return;

    long idx = pDisp->item.iItem;      // 列索引
    long sub = pDisp->item.iSubItem;   // 欄索引

    try {
        m_pRS->SetAbsolutePosition(idx); // 移動至指定記錄
        COleVariant var;
        m_pRS->GetFieldValue(sub == 0 ? 0 : sub, var); // 取得欄位值

        CString str = FormatVariant(var); // 依型別格式化
        lstrcpyn(pDisp->item.pszText, str, pDisp->item.cchTextMax);
    }
    catch (CDaoException*) { /* 忽略錯誤 */ }

    if (pDisp->item.mask & LVIF_IMAGE) pDisp->item.iImage = 0;
    *pResult = 0;
}

4. Variant 型別格式化輔助函式

CString FormatVariant(const COleVariant& var) {
    switch (var.vt) {
        case VT_I2: return CString().Format(_T("%d"), var.iVal);
        case VT_I4: return CString().Format(_T("%d"), var.lVal);
        case VT_R4: return CString().Format(_T("%.2f"), var.fltVal);
        case VT_R8: return CString().Format(_T("%.2f"), var.dblVal);
        case VT_CY: { COleCurrency c(var); return c.Format(); }
        case VT_DATE: { COleDateTime t(var.date); return t.Format(_T("%Y-%m-%d")); }
        case VT_BSTR: return CString(var.bstrVal);
        case VT_BOOL: return var.boolVal ? _T("TRUE") : _T("FALSE");
        default: return _T("");
    }
}

效能優化建議

  • 記錄集保持開啟,避免重複查詢。
  • 若資料極大(百萬筆以上),考慮改用 ODBC 快照或直接 SQL 查詢配合 LVM_SETITEMCOUNT 延遲載入。
  • 啟用 LVSICF_NOINVALIDATEALL 避免設定項目數時全域失效重繪。

總結

虛擬清單模式將資料存儲與顯示解耦,配合動態集隨機存取特性,能以極低記憶體佔用流暢顯示百萬級記錄,是 MFC 處理大量表格資料的標準解法。

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