Search code examples
c++visual-studio-2010mfcmfc-feature-pack

Strange (?) behaviour while sorting CMFCListCtrl


I have a subclass of CMFCListCtrl that supports sorting, reading the documentation I have to implement the virtual method:

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

where lParam1 and lParam2 are params of some sort, specified when the list control is created.

Since I need an alphabetical sort I've inserted my rows in this way:

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

At this point, my Subclass sorting method:

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

works greatly. But now, because of sorting, my LPARAMs are no more correct since they represent the row number. I then handled the header click notification, which happens after the sorting:

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

it might be the worst practice, but for now it works pretty good: I set the new LPARAMs according with the new row numbers. I also picked up text before and after point (1) to check if I updated the right row and everything seems fine: I got old param first and new param then.

What now: I clicked another time in the row header to sort descending and what I get? Everything scrumbled. I decided to check some stuff and I added the following code:

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

restarted the program, sort the list again and clicking the first row (after having checked again that the new LPARAMs are correct) I got the message:

PARAM: 10.

while I should get:

PARAM: 0.

10 is the old position. Is there something that I am missing somewhere?


Solution

  • Problem solved: it was in

    // bla bla about item data
    

    where I update the data with the member:

    SetItemData(int, DWORD_PTR);
    

    according to https://msdn.microsoft.com/it-it/library/936147y4.aspx ItemData is actually LPARAM value specified in LVITEM structure.