Frage

I want a CListCtrl to always have a selected item, like a collection of radio buttons.

I have used the styles: LVS_SHOWSELALWAYS|LVS_SINGLESEL

I have been looking for a style for this, but haven't been able to find this.

War es hilfreich?

Lösung

I don't think there's built-in support for this.

The part of having a selection from the start is easy: just select an item after populating the list:

// Populate the list
// ...

c_MyList.SetItemState(nItem, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);

The other part, preventing the deselection of all items when the user clicks outside all the items, is trickier. You can easily detect if the list is deselecting an item, but not why the item is losing the selected state. I.e. you can't tell if it is deselecting it to select another one or to leave all the items unselected. The reason is the control first sends the "item X has been deselected" notification, then the "item Y has been selected" notification. If no item is selected, you get the first one, but not the second one.

A little idea I've thought of is to catch NM_CLICK notifications and prevent the control from deselecting the item. The problem is NM_CLICK is sent after all the select/deselect notifications.

So, this is the little hack I've come up with: when an item loses the selected status, store its item index. Then in the NM_CLICK notification, if the activated item is -1, select again the last unselected item:

void CMyDialog::OnLvnItemchangedListaEjesPane(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);

    if (pNMLV->uChanged & LVIF_STATE)
    {
        UINT oldSelectionState = (pNMLV->uOldState & LVIS_SELECTED);
        UINT newSelectionState = (pNMLV->uNewState & LVIS_SELECTED);

        if ( oldSelectionState == LVIS_SELECTED && newSelectionState == 0 )
        {   // Deselect item
            m_LastDeselectedItem = pNMLV->iItem;
        }
        // ...
    }

    *pResult = 0;
}

void CMyDialog::OnNMClickListaEjesPane(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);

    if (pNMItemActivate->iItem == -1)
    {
        c_ListaEjes.SetItemState(m_LastDeselectedItem, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
    }
    *pResult = 0;
}

It may not suit your needs fully, because it actually deselects the item then selects it again.

There are probably better solutions, involving creating your own CListCtrl subclass and intercepting the clicks, checking if they are going to force a null selection and preventing it, but you have to decide if the extra work is worth the trouble.

Andere Tipps

Handle WM_LBUTTONDOWN. In a CListCtrl-derived class, add

MyListCtrl.cpp:

BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
    ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

void CMyListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
    UINT uHitFlags;
    int nItem = HitTest(point, &uHitFlags);

    if (uHitFlags & LVHT_NOWHERE)
    {
        // eat the message by just returning
        return;
    }

    CListCtrl::OnLButtonDown(nFlags, point);
}

This will prevent the mouse click from going to the control and eat the message. You will still be able to remove the selection programmatically, but the user won't be able to click anywhere in the blank area below your items to remove the selection.

This can be done simply in the dialog editor when you design your clistctrl, set the "Always Show Selection" and "Single Selection" options in the Properties to be true. If you want to do this via code, then you have the right way, but prob the wrong place. Override OnInitDialog and in it call CDialog::OnInitDialog first, then set the new style after that(use ModifyStyle). The problem is the base class is overriding your style changes. Other than that, it could be a few things, MFC isnt known for its intuitiveness!

I solved this problem as follows:

  1. I add in dialog new variable named lastItem
  2. handle LVN_ITEMCHANGING message and add this lines:

    lastItem = pNMLV->iItem;
    
  3. handle NM_CLICK, NM_DBLCLK, NM_RCLICK and NM_RDBLCLK messages and add these lines to all messages:

    CListCtrl * listCtrl = (CListCtrl *)FromHandle(pNMHDR->hwndFrom);
    if (pNMItemActivate->iItem == -1)
        listCtrl->SetItemState(m_lastItem, LVIS_SELECTED, LVIS_SELECTED);
    
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top