对CMFCLISTCTRL排序时奇怪的(?)行为

Strange (?) behaviour while sorting CMFCListCtrl

本文关键字:行为 CMFCLISTCTRL 排序      更新时间:2023-10-16

我有一个支持排序的cmfclistctrl的子类,读取我必须实现的文档虚拟方法:

virtual int OnCompareItems(LPARAM lParam1, LPARAM lParam2, int nColumn);

其中lparam1和lparam2是某种参数,在创建列表控件时指定。

由于我需要按字母顺序排列的,因此我以这种方式插入了我的行:

item.mask = LVIF_COLFMT | LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT;
for (nI = 0; nI ....)
{
    // UMASK, ITEM IDX, TEXT, STATE 1, STATE 2, IMAGE IDX, LPARAM
    m_lstMateriali.InsertItem(item.mask, nI, _T("   "), 0, 0, SelezionaIcona(itIM->m_bSelez, itIM->m_eStatus), nI);
    strQnt.Format(_T("%d"), itIM->m_nIdBolla);
    m_lstMateriali.SetItemText(nI, 1, strQnt);
    m_lstMateriali.SetItemText(nI, 2, itIM->m_strCodMater);
    m_lstMateriali.SetItemText(nI, 3, itIM->m_strDescrMater);
    m_lstMateriali.SetItemText(nI, 4, itIM->m_strColore);
    strQnt.Format(_T("%d"), itIM->m_nDaTag);
    m_lstMateriali.SetItemText(nI, 5, strQnt);
    m_lstMateriali.SetItemData(nI, nI);
}

此时,我的子类分类方法:

virtual int OnCompareItems(LPARAM lParam1, LPARAM lParam2, int nColumn)
{
    int     nCol_1;             ///< 
    int     nCol_2;             ///< 
    CString strCol_1;           ///< 
    CString strCol_2;           ///< 
    strCol_1 = GetItemText(lParam1, nColumn);
    strCol_2 = GetItemText(lParam2, nColumn);
    switch (nColumn)
    {
    case 2:
        return strCol_1.Compare(strCol_2);
    }
}

效果很大。但是现在,由于排序,我的lparams不再正确,因为它们代表了行号。然后,我处理了标头单击通知,这发生在之后发生的

void CEliCUTK2SceltaMaterialiDlg::OnHdnItemclickLstSmListapezzi(NMHDR * pNMHDR, LRESULT * pResult)
{
    int         nNumRig;
    CString     strCodMat;
    LPNMHEADER  phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
    LVITEM      item;
    nNumRig = m_lstMateriali.GetItemCount();
    memset(&item, 0, sizeof(LVITEM));
    item.mask = LVIF_PARAM;
    for (int nI = 0; nI < nNumRig; nI++)
    {
        item.iItem = nI;
        item.lParam = nI;
        m_lstMateriali.SetItem(&item);  // (1)
        strCodMat = m_lstMateriali.GetItemText(nI, 2);
        // bla bla about item data
    }
    *pResult = 0;
}

这可能是最糟糕的做法,但就目前而言,它很好:我将新的lparams设置为新的行号。我还在第(1)点之前和之后拾取了文字,以检查我是否更新了行,一切似乎都很好:我先获得了旧的param和新的param。

现在是什么:我在行标题中再次单击了排序降序以及得到什么?一切都很混乱。我决定检查一些内容,然后添加了以下代码:

void CEliCUTK2SceltaMaterialiDlg::OnLvnItemchangedLstSmListapezzi(NMHDR * pNMHDR, LRESULT * pResult)
{
    int             nSot, nTip;
    CString         strID;
    DWORD_PTR       dwID;
    LPNMLISTVIEW    pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
    LVITEM          item;
    if (pNMLV->uNewState == 3)
    {
        dwID = m_lstMateriali.GetItemData(pNMLV->iItem);
        memset(&item, 0, sizeof(LVITEM));
        item.mask = LVIF_PARAM;
        item.iItem = pNMLV->iItem;
        m_lstMateriali.GetItem(&item);
        strID.Format(_T("PARAM: %ld."), item.lParam);
        AfxMessageBox(strID);
    }
    *pResult = 0;
}

重新启动程序,再次对列表进行排序,然后单击 first row(在再次检查新的LPARAMS之后正确)我收到了消息:

PARAM: 10.

我应该得到:

PARAM: 0.

10是旧位置。我在某个地方有一些东西吗?

通常,您要在lparam中存储的东西可以使您可以回到用来填充列表的数据,或者是包含包含的结构的指针比较的数据。如果您有一个并行的Clist或STD :: List Collection(或Carray或STD :: vector),则将索引起作用,该索引在ClistCtrl的一生中不会更改其订单。

在进行分类或插入过程中,列表中的项目10在您的示例中可能具有2个。您想使用" 2"将原始数据源(Clist,Carray,记录集或其他内容)索引,然后在此处获取数据并在原始数据上使用您的比较。因为,当您排序时,LPARAM中的索引将与ClistCtrl(或CMFClistCtrl)中的索引相同。

您可以分配比较所需的所有数据的结构,并将其存储在LPARAM成员中。(在某些MFC类中,您使用setItemDataptr)。要清理数据,请处理lvn_deleteitem消息。

解决问题:它在

// bla bla about item data

在哪里更新数据的位置:

SetItemData(int, DWORD_PTR);

根据https://msdn.microsoft.com/it-it/library/936147y4.4.4.4.aspx ItemData实际上是LVITEM结构中指定的LPARAM值。