ListCtrl插入大量數據時,發現緩慢有問題,QT裏有數據和顯示分開,MFC也有比較戳的虛擬表,古老的技術

最初代碼來源

https://www.codeproject.com/Articles/7891/Using-virtual-lists

csdn代碼下載

https://download.csdn.net/download/rembo254/2100719

 

知識點:

subclasswindow是更改窗口過程,就是類嚮導關聯自定義的列表控件會調用PreSubclassWindow

CreateWindowEx會調用PreCreateWindow,這個是動態創建的,筆者重載了這個函數,發現一直沒有調用,花了很長的時間去調試

 

#pragma once
#include <vector>



class CSampleData
{
public:
    CString moduleName;  // 模塊名稱
    CString filePath;  // 圖片路徑
    int result; // 分析結果
    int image; // 列表圖片
    bool checked; // 複選框
};


// 虛擬列表,記得RC編輯界面改對話框屬性Owner Data爲true
class CVirtualListCtrl : public CListCtrl
{
	DECLARE_DYNAMIC(CVirtualListCtrl)

public:
	CVirtualListCtrl();
	virtual ~CVirtualListCtrl();

public:
    void AddItem(const CSampleData& data);
    void Clear();
    DWORD GetItemCount() { return m_database.size(); }

protected:
	DECLARE_MESSAGE_MAP()
    virtual void PreSubclassWindow()override;
    afx_msg void OnLvnGetdispinfo(NMHDR *pNMHDR, LRESULT *pResult);
    afx_msg void OnLvnOdfinditem(NMHDR *pNMHDR, LRESULT *pResult);
    afx_msg void OnLvnOdcachehint(NMHDR *pNMHDR, LRESULT *pResult);

private:
    BOOL IsCheckBoxesVisible();
    void ToggleCheckBox(int item);




private:
    std::vector<CSampleData> m_database;
public:
    afx_msg void OnNMClick(NMHDR *pNMHDR, LRESULT *pResult);
    afx_msg void OnLvnKeydown(NMHDR *pNMHDR, LRESULT *pResult);

};


// VirtualListCtrl.cpp : 實現文件
//

#include "stdafx.h"
#include "VListTest.h"
#include "VirtualListCtrl.h"


// CVirtualListCtrl

IMPLEMENT_DYNAMIC(CVirtualListCtrl, CListCtrl)

CVirtualListCtrl::CVirtualListCtrl()
{

}

CVirtualListCtrl::~CVirtualListCtrl()
{
}

void CVirtualListCtrl::AddItem(const CSampleData & data)
{
    m_database.push_back(data);
    SetItemCount(m_database.size());
}

void CVirtualListCtrl::Clear()
{
    m_database.clear();
    SetItemCount(0);
}


BEGIN_MESSAGE_MAP(CVirtualListCtrl, CListCtrl)
    ON_NOTIFY_REFLECT(LVN_GETDISPINFO, &CVirtualListCtrl::OnLvnGetdispinfo)
    ON_NOTIFY_REFLECT(LVN_ODFINDITEM, &CVirtualListCtrl::OnLvnOdfinditem)
    ON_NOTIFY_REFLECT(LVN_ODCACHEHINT, &CVirtualListCtrl::OnLvnOdcachehint)
    ON_NOTIFY_REFLECT(NM_CLICK, &CVirtualListCtrl::OnNMClick)
    ON_NOTIFY_REFLECT(LVN_KEYDOWN, &CVirtualListCtrl::OnLvnKeydown)
    ON_WM_CREATE()
END_MESSAGE_MAP()



// CVirtualListCtrl 消息處理程序

void CVirtualListCtrl::PreSubclassWindow()
{
    // TODO: 在此添加專用代碼和/或調用基類
    ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
    SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_GRIDLINES);
    CListCtrl::PreSubclassWindow();
}



// 虛擬列表顯示前,發送消息請求顯示的內容
void CVirtualListCtrl::OnLvnGetdispinfo(NMHDR *pNMHDR, LRESULT *pResult)
{
    LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;

    LV_ITEM* pItem = &(pDispInfo)->item;
    int itemid = pItem->iItem;
    if (pItem->mask & LVIF_TEXT)
    {
        CString text;
        if (pItem->iSubItem == 0){
            text = m_database[itemid].moduleName;
        }
        else if (pItem->iSubItem == 1){
            text = m_database[itemid].filePath;
        }
        lstrcpyn(pItem->pszText, text, pItem->cchTextMax);
    }


    if (pItem->mask & LVIF_IMAGE)
    {
        // pItem->iImage = m_database[itemid].m_image;
        if (IsCheckBoxesVisible())
        {
            pItem->mask |= LVIF_STATE;
            pItem->stateMask = LVIS_STATEIMAGEMASK;

            if (m_database[itemid].checked) {
                pItem->state = INDEXTOSTATEIMAGEMASK(2);  // Turn check box on..
            }
            else {
                pItem->state = INDEXTOSTATEIMAGEMASK(1);  // Turn check box off
            }
        }
    }

    *pResult = 0;
}


// 快捷鍵查找項
void CVirtualListCtrl::OnLvnOdfinditem(NMHDR *pNMHDR, LRESULT *pResult)
{
    // pNMHDR has information about the item we should find
    // In pResult we should save which item that should be selected
    NMLVFINDITEM* pFindInfo = (NMLVFINDITEM*)pNMHDR;

    /* pFindInfo->iStart is from which item we should search.
    We search to bottom, and then restart at top and will stop
    at pFindInfo->iStart, unless we find an item that match
    */

    // Set the default return value to -1
    // That means we didn't find any match.
    *pResult = -1;

    //Is search NOT based on string?
    if ((pFindInfo->lvfi.flags & LVFI_STRING) == 0)
    {
        //This will probably never happend...
        return;
    }

    /*
    Let's look on a sample list;

    Name
    Anders
    * Anna
    Annika
    Bob
    Emma
    Emmanuel

    Anna is selected.
    If "A" is written, Annika should be selected.
    If "AND" is written, Anders should be selected.
    If "ANNK" is written, the selection should stay on Anna.
    If "E" is written, Emma should be selected.

    */

    //This is the string we search for
    CString searchstr = pFindInfo->lvfi.psz;

    //	TRACE(_T("Find: %s\n"), searchstr);

    int startPos = pFindInfo->iStart;
    //Is startPos outside the list (happens if last item is selected)
    if (startPos >= GetItemCount())
        startPos = 0;

    int currentPos = startPos;

    //Let's search...
    do
    {
        //Do this word begins with all characters in searchstr?
        if (_tcsnicmp(m_database[currentPos].moduleName, searchstr, searchstr.GetLength()) == 0)
        {
            //Select this item and stop search.
            *pResult = currentPos;
            break;
        }

        //Go to next item
        currentPos++;

        //Need to restart at top?
        if (currentPos >= GetItemCount())
            currentPos = 0;

        //Stop if back to start
    } while (currentPos != startPos);
}


// 數據庫在網絡上另一臺電腦需要使用
void CVirtualListCtrl::OnLvnOdcachehint(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLVCACHEHINT pCacheHint = reinterpret_cast<LPNMLVCACHEHINT>(pNMHDR);
    // TODO: 在此添加控件通知處理程序代碼
    *pResult = 0;
}

BOOL CVirtualListCtrl::IsCheckBoxesVisible()
{
    DWORD style = GetStyle();

    if (((style & LVS_TYPEMASK) == LVS_LIST) ||
        ((style & LVS_TYPEMASK) == LVS_REPORT))
        return TRUE;

    return FALSE;
}

void CVirtualListCtrl::ToggleCheckBox(int item)
{
    m_database[item].checked = !m_database[item].checked;
    RedrawItems(item, item);
}


void CVirtualListCtrl::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
{
    NMLISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

    LVHITTESTINFO hitinfo;
    hitinfo.pt = pNMListView->ptAction;

    int item = HitTest(&hitinfo);
    if (item != -1) {
        if ((hitinfo.flags & LVHT_ONITEMSTATEICON) != 0) {
            ToggleCheckBox(item);
        }
    }


    *pResult = 0;
}


void CVirtualListCtrl::OnLvnKeydown(NMHDR *pNMHDR, LRESULT *pResult)
{
    LV_KEYDOWN* pLVKeyDown = (LV_KEYDOWN*)pNMHDR;

    if (pLVKeyDown->wVKey == VK_SPACE) {
        if (IsCheckBoxesVisible()) {
            if (GetSelectionMark() != -1)
                ToggleCheckBox(GetSelectionMark());
        }
    }

    *pResult = 0;
}




 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章