0

我从 CMFCListControl 子类化并在报表视图 (LVS_REPORT) 中创建一个列表控件来显示数据。由于我的数据包含一百万条记录,因此填充列表控件的 AF 前期速度很慢(平均 6 分钟),但之后非常流畅。我使用 LVS_OWNERDATA 窗口样式切换到虚拟列表控件。它工作得更好(调试时为 75 秒),但是,在初始显示之后,任何滚动尝试都非常缓慢。它可以工作,并且永远不会崩溃,但需要 2 分钟才能显示更改。读取的数据在内存中的 std::map 中,因此磁盘驱动器或网络延迟问题不是原因。

void CAlbumListCtrl::OnLvnGetdispinfo(NMHDR *pNMHDR, LRESULT *pResult)
{
    NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
    LVITEM &Item = (pDispInfo)->item;
    CString csTemp;
    
    ASSERT(m_pLibrary);
    if (!m_pLibrary)
        AfxThrowMemoryException();
    
    const CImageEntry *pEntry = m_pLibrary->GetImageEntryAt((size_t)Item.iItem);
    //pListCtrl->SetItemText(nCount, 1, pEntry->GetItemName().c_str());
    //pListCtrl->SetItemText(nCount, 2, pEntry->GetPathName().c_str());
    //pListCtrl->SetItemText(nCount, 3, pLib->GetImageEntryType(pEntry).c_str());
    
    if (Item.mask & LVIF_TEXT) //valid text buffer?
    {
        switch (Item.iSubItem)
        {
        case 0: //fill in ID
            //_tcscpy_s(Item.pszText, Item.cchTextMax,
            //  m_Items[iItem].m_strItemText);
            csTemp.Format(_T("%ld"), pEntry->GetItemId());
            _tcscpy_s(Item.pszText, Item.cchTextMax, csTemp);
            break;
        case 1: //fill in sub item 1 text
            _tcscpy_s(Item.pszText, Item.cchTextMax, pEntry->GetItemName().c_str());
            break;
        case 2: //fill in sub item 2 text
            _tcscpy_s(Item.pszText, Item.cchTextMax, pEntry->GetPathName().c_str());
            break;
        case 3: //fill in sub item 1 text
            _tcscpy_s(Item.pszText, Item.cchTextMax, m_pLibrary->GetImageEntryType(pEntry).c_str());
            break;
        //case 2: //fill in sub item 2 text
            //  _tcscpy_s(Item.pszText, Item.cchTextMax, pEntry->GetPathName().c_str());
            //  break;
        default:
            break;
        }
    }
    
    *pResult = 0;
}
int CAlbumView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    m_wndTabCtrl.Create(TCS_TABS | TCS_FIXEDWIDTH | WS_CHILD | WS_VISIBLE,
            CRect(0, 0, 20, 20), this, IDC_ALBUMVIEW_HEADERTAB);
    
    if (!m_wndTabCtrl.m_hWnd)
        return -1;
    
    m_wndTabCtrl.InsertItem(0, _T("List View"));
    m_wndTabCtrl.InsertItem(1, _T("Tree View"));
    
    m_listCtrl.Create(LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA | WS_VISIBLE, CRect(0, 0, 20, 20), this, IDC_LISTCTRL);
    m_listCtrl.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_ONECLICKACTIVATE);
    m_listCtrl.InsertColumn(0, _T("ID"), LVCFMT_CENTER, 150);
    m_listCtrl.InsertColumn(1, _T("Nickname"), LVCFMT_CENTER, 100);
    m_listCtrl.InsertColumn(2, _T("Image File Pathname"), LVCFMT_CENTER, 500);
    m_listCtrl.InsertColumn(3, _T("Image Type"), LVCFMT_CENTER, 100);
    m_listCtrl.InsertColumn(4, _T("Dimensions"), LVCFMT_CENTER, 75);
    m_listCtrl.InsertColumn(5, _T("Color Depth"), LVCFMT_CENTER, 95);
    m_listCtrl.InsertColumn(6, _T("Tags"), LVCFMT_CENTER, 150);
        
    m_treeCtrl.Create(TVS_HASBUTTONS | WS_VISIBLE, CRect(0, 0, 20, 20), this, IDC_TREECTRL);
    
    SetVisibleViewCtrl(0);
    
    return 0;
}
void CAlbumView::UpdateView()
{
    std::map<wstring, vector<wstring>> mapImageFilesByFolder;
    time_t start = time(NULL);
    auto *pFrame = GetParentFrame();
    size_t nCount = 0;
    if (pFrame)
    {
        CImageLibrary *pLib = pFrame->GetImageLibrary();
        CAlbumView *pView = pFrame->GetAlbumView();
        CAlbumListCtrl *pListCtrl = pView->GetListCtrl();
        CAlbumTreeCtrl *pTreeCtrl = pView->GetTreeCtrl();
        CUpdatingViewDlg dlg;
        dlg.Create(this);
        dlg.ShowWindow(SW_SHOW);
        dlg.CenterWindow();
        dlg.RedrawWindow();
        pListCtrl->SendMessage(WM_SETREDRAW, FALSE);
        pTreeCtrl->SendMessage(WM_SETREDRAW, FALSE);
        nCount = pLib->GetImageCount();
        pListCtrl->SetItemCountEx((int)nCount);
        pListCtrl->SetImageLibrary(pLib);
        SetThreadPriority(GetCurrentThread(), 7);
        POSITION pos = pLib->EnumImageEntries();
        if (::IsWindow(pListCtrl->m_hWnd))
        {
            pListCtrl->DeleteAllItems();
            pTreeCtrl->DeleteAllItems();
            int nCount = 0;
            while (pos != (POSITION)-1)
            {
                CString csTemp;
                const CImageEntry *pEntry = pLib->GetCurrentImageEntry(pos);
                csTemp.Format(_T("%ld"), pEntry->GetItemId());
                pListCtrl->InsertItem(nCount, csTemp);
                //pListCtrl->SetItemText(nCount, 1, pEntry->GetItemName().c_str());
                //pListCtrl->SetItemText(nCount, 2, pEntry->GetPathName().c_str());
                //pListCtrl->SetItemText(nCount, 3, pLib->GetImageEntryType(pEntry).c_str());
                //pTreeCtrl->ParseAndAddFile(pEntry->GetPathName().c_str());
                pos = pLib->GetNextPos(pos);
                ++nCount;
            }
    
            CString csTemp;
            csTemp.Format(_T("Done in %d seconds."), time(NULL) - start);
            MessageBox(csTemp, _T("X"));
    
            SetThreadPriority(GetCurrentThread(), 0);
            pListCtrl->SendMessage(WM_SETREDRAW, TRUE);
            pListCtrl->RedrawWindow(NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
            pTreeCtrl->SendMessage(WM_SETREDRAW, TRUE);
            pTreeCtrl->RedrawWindow(NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
            dlg.ShowWindow(SW_HIDE);
            dlg.DestroyWindow();
        }
    }
}
4

1 回答 1

2

因此,您创建了一个LVS_OWNERDATA(虚拟)列表视图控件并将其样式设置为LVS_REPORT. 顺便说一句,(0,0,20,20)的矩形不是太小了吗?但我看到您对其他控件也使用相同的坐标,所以我猜您稍后会重新排列它们(此处未显示代码)。

处理LVN_GETDISPINFO通知的函数必须在类的消息映射中声明,以将函数与消息关联:

BEGIN_MESSAGE_MAP(CAlbumView, ...)
    .
    .
    ON_NOTIFY(LVN_GETDISPINFO, IDC_LISTCTRL, &CAlbumView::OnLvnGetdispinfoListCtlr)
END_MESSAGE_MAP()

这就是向导生成的代码的样子,如果您没有从子类化CMFCListControl而是简单地CMFCListControl按原样使用。但是您自己创建了控件,因此如果您在子类的消息映射中正确地进行了上述声明并且发现该函数实际上被调用(调试或跟踪),请忽略此注释。

我认为您的代码不起作用的原因是因为您将项目的文本复制到应该由 the 指向的缓冲区,pszText而不是设置pszText指针。以下是有关LV_ITEM结构的文档摘录:

pszText
如果结构指定项目属性,则 pszText 是指向包含项目文本的以空字符结尾的字符串的指针。响应 LVN_GETDISPINFO 通知时,请确保该指针保持有效,直到收到下一个通知。
另外:
cchTextMax
此成员仅在结构接收项目属性时使用。...在 LVN_GETDISPINFO 和其他 LVN_ 通知期间它是只读的。

LVN_GETDISPINFO文档中的示例正是这样做的。

因此,您的代码可以更改如下所示:

    void CAlbumListCtrl::OnLvnGetdispinfo(NMHDR *pNMHDR, LRESULT *pResult)
    {
        NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
        LVITEM &Item = (pDispInfo)->item;
        CString csTemp;
        static TCHAR _szItemId[20]; // Persistent buffer
        
        ASSERT(m_pLibrary);
        if (!m_pLibrary)
            AfxThrowMemoryException();
        
        const CImageEntry *pEntry = m_pLibrary->GetImageEntryAt((size_t)Item.iItem);
        
        if (Item.mask & LVIF_TEXT) //Item/subItem text
        {
            switch (Item.iSubItem)
            {
            case 0: //fill in ID
                csTemp.Format(_T("%ld"), pEntry->GetItemId());
                 _tcscpy_s(_szItemId, csTemp);
                Item.pszText = _szItemId;
                break;
            case 1: //fill in sub item 1 text
                Item.pszText = pEntry->GetItemName().c_str();
                break;
            case 2: //fill in sub item 2 text
                Item.pszText = pEntry->GetPathName().c_str();
                break;
            case 3: //fill in sub item 3 text
                Item.pszText = m_pLibrary->GetImageEntryType(pEntry).c_str();
                break;
            default:
                break;
            }
        }
        
        *pResult = 0;
    }

笔记:

  • 我只是在文本编辑器中修改了这段代码,实际上并没有经过测试。它甚至可能需要一些小的修复才能编译。
  • InsertItem()必须删除and调用(以及列表的DeleteAllItems()浏览),因为控件实际上并不存储任何内容,而是通过 LVN_GETDISPINFO 消息(由项目的索引 - 引用iItem)请求正在显示的项目的数据。
于 2022-01-22T00:44:49.773 回答