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;
}




 

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