How to virtual List control using a map data structure'?

206 Views Asked by At

I have a question while studying C++ MFC.

void AnchorDlg::OnGetdispinfoListctrl(NMHDR *pNMHDR, LRESULT *pResult)
{

    NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);

    LV_ITEM* pItem = &(pDispInfo)->item;
    CString str;
    if(pItem == NULL) return;

    int nRow = pItem->iItem;
    int nCol = pItem->iSubItem;

    if(nRow<0 || nRow >= mapShip->size()) return;

    auto iter = mapShip->begin();

    if(pItem->pszText)
    {
        switch(nCol)
        {
        case 1:
            str.Format(_T("%.0f"), iter->second->mmsi);
            //str.Format(_T("%.0f"), iter->second->mmsi);
            lstrcpy(pItem->pszText, str);
            break;
        case 2:
            str.Format(_T("%.7f"), iter->second->lat);
            lstrcpy(pItem->pszText, str);
            break;
        case 3:
            str.Format(_T("%.7f"), iter->second->lng);
            lstrcpy(pItem->pszText, str);
            break;
        case 4:
            str.Format(_T("%.1f"), iter->second->sog);
            lstrcpy(pItem->pszText, str);
        case 5:
            str.Format(_T("%.1f"), iter->second->cog);
            lstrcpy(pItem->pszText, str);
        }
    }
    *pResult = 0;
}

mapShip consists of double and data objects.

I want to print out 1000 data, but only one data is printed.

I've used iter but only one data is output. Same data print repeat.

I must used map data structure.

I don't know how to use the map.

2

There are 2 best solutions below

6
Constantine Georgiou On BEST ANSWER

You don't seem to do anything with pItem->iItem (nRow), you just set it to the beginning of the list. You should instead search your data with this - requests may arrive in any random order. You don't have to iterate the list in OnGetdispinfoListctrl(), instead you should return the data to be displayed, given the iItem and iSubItem members. So consider using a more efficient structure to hold your data, if they are really too many (lists are not, because they are serial access structures, not random access ones). Also, don't copy the text into Item.pszText, instead set Item.pszText to point to your data. The if(pItem->pszText) check is not needed, neither is correct:

void AnchorDlg::OnGetdispinfoListctrl(NMHDR *pNMHDR, LRESULT *pResult)
{
    NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);

    LV_ITEM* pItem = &(pDispInfo)->item;

    // Persistent Buffer
    static CString str;

    if (pItem == NULL) return;

    int nRow = pItem->iItem;
    int nCol = pItem->iSubItem;

    if (nRow < 0 || nRow >= mapShip->size()) return;

    if (Item.mask & LVIF_TEXT) //Item/subItem text
    {
        //auto iter = mapShip->begin();
        auto pValue = SearchMyData(nRow);

        switch (nCol)
        {
            case 1:
                str.Format(_T("%.0f"), pValue->mmsi);
                break;
            case 2:
                str.Format(_T("%.7f"), pValue->lat);
                break;
            case 3:
                str.Format(_T("%.7f"), pValue->lng);
                break;
            case 4:
                str.Format(_T("%.1f"), pValue->sog);
                break;
            case 5:
                str.Format(_T("%.1f"), pValue->cog);
                break;
        }
        pItem->pszText = const_cast<LPTSTR>((LPCTSTR)str);
    }
    *pResult = 0;
}

EDIT:

Here is a excerpt from the documentation about the LV_ITEM structure:

pszText
If the structure specifies item attributes, pszText is a pointer to a null-terminated string containing the item text. When responding to an LVN_GETDISPINFO notification, be sure that this pointer remains valid until after the next notification has been received.

Also: cchTextMax
This member is only used when the structure receives item attributes. ... It is read-only during LVN_GETDISPINFO and other LVN_ notifications.

And the example in the LVN_GETDISPINFO documentation does exactly this, ie sets the pszText pointer rather than copies text into it.

Also, you shouldn't get the conversion error if you are using either the multibyte (ANSI) or the wide (Unicode) version of both the LVITEM and CString, which you seem to do (you do not set them explicitly, which is OK, it defaults to T interpretation). It must be a const conflict. So, either use a const_cast:

    pItem->pszText = const_cast<LPTSTR>((LPCTSTR)str);

or a char array instead:

    static TCHAR _szItem[MAX_LEN]; // Persistent buffer
    .
    .
    CString str;
    str.Format(_T("%.0f"), iter->second->mmsi));
    _tcscpy_s(_szItem, str);
    pItem->pszText = _szItem;
    break;
0
IInspectable On

A virtual list-view control is a list-view control that doesn't store any data. Instead, users just tell the control how many items there are (by sending a LVM_SETITEMCOUNT message), and the control implementation will subsequently call back to the user to provide the data for the individual cells (by way of the LVN_GETDISPINFO message).

While the code in question is responding to the LVN_GETDISPINFO message, evaluating the nRow (and nCol) identifying the cell to be displayed, it doesn't use that data for information lookup. The code perpetually accesses the first item (mapShip->begin()) for display. However, it should be using nRow to index into the data represented by mapShip (see How to Use Virtual List-View Controls for an example).

Assuming that mapShip is an instantiation of the std::map class template, you'll want to index into that map using nRow as the index, e.g.:

auto const& value = mapShip[nRow];

and use value in place of iter. This will display items in the order they are stored in the associative container (sorted by key). If you need a different ordering, you'll have to map nRow onto the respective index in the map.