仅在MFC中的CListCtrl复选框中进行单选

时间:2013-12-16 11:30:09

标签: checkbox mfc clistctrl

在MFC对话框中,我使用了带复选框的CListCtrl。我想禁用多个复选框选择,这样用户一次只能选择一个复选框。实现这一目标的最佳方法是什么。我已经完成了这个

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


        int nSelectedItemIndex = -1;
        nSelectedItemIndex = m_ListCtrl.GetNextItem(-1, LVNI_SELECTED);
        int nCount = m_ListCtrl.GetItemCount();    
        for(int nItem = 0; nItem < nCount; nItem++)
        {
            m_ListCtrl.SetCheck(nItem,false);
        }
        if(nSelectedItemIndex != -1)
            m_ListCtrl.SetCheck(nSelectedItemIndex,true);

        *pResult = 0;
    }

不知何故,我觉得这种方法不太合适,可以用其他方式做得更好。所有的建议都受到欢迎。

编辑:更新:编写代码后,一切正常,但我遇到了一个新问题。 在OnItemChanged消息处理函数中调用SetCheck()函数,它再次调用相同的函数,创建一个递归。因此选择更改有点慢。如何避免这种情况。请帮忙。 ????

3 个答案:

答案 0 :(得分:2)

最后我解决了它。现在复选框和选择工作在par。选择复选框选择行,反之亦然,可以进行一次选择。代码:

    void SomeClass::ResetAllCheckBox()
    {
        int nCount = m_ListCtrl.GetItemCount();
        for(int nItem = 0; nItem < nCount; nItem++)
        {
            m_ListCtrl.SetCheck(nItem,false);
        }

    }

    //Handler for ON_NOTIFY(NM_CLICK,...)
    void SomeClass::OnClickList(NMHDR *pNMHDR, LRESULT *pResult)
    {
        LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
        NMLISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
        LVHITTESTINFO hitinfo;
        int nPosCB=-1,nPos=-1;
        hitinfo.pt = pNMListView->ptAction;

        //Make the hit test...
        nPosCB = m_ListCtrl.HitTest(&hitinfo);

        if(hitinfo.flags != LVHT_ONITEMSTATEICON)
            return;
        ResetAllCheckBox();

        nPos = m_ListCtrl.GetNextItem(-1,LVNI_SELECTED);
        m_ListCtrl.SetItemState(nPos, ~LVIS_SELECTED, LVIS_SELECTED);
        m_ListCtrl.SetItemState(nPosCB, LVIS_SELECTED, LVIS_SELECTED);
        m_ListCtrl.SetSelectionMark(nPosCB);
        *pResult = 0;
    }


    //Handler for ON_NOTIFY(LVN_ITEMCHANGED,...)
    void SomeClass::OnItemchangedList(NMHDR *pNMHDR, LRESULT *pResult)
    {
        LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
        int nPos = -1;

        ResetAllCheckBox();

        nPos = m_ListCtrl.GetNextItem(-1,LVNI_SELECTED);
        if(nPos != -1)
            m_ListCtrl.SetCheck(nPos);

        int nCount = m_ListCtrl.GetItemCount();
        int nSelectedItemIndex = -1;
        for(int nItem = 0; nItem < nCount; nItem++)
        {
            if(m_ListCtrl.GetCheck(nItem)== 1)
                nSelectedItemIndex = nItem;
        }

        *pResult = 0;
    }

答案 1 :(得分:1)

创建控件时,请确保使用此样式LVS_SINGLESEL

它在CreateEx / CreateEx函数中传递。也可以从资源编辑器获得(如果通过它添加控件)。

答案 2 :(得分:0)

我找到了一个更短的解决方案。这个想法是要检测用户是否选中了一个复选框。如果是这种情况,请取消选中所有其他复选框,否则不执行任何操作。与@egur的答案相反,此解决方案不会在选择行时选中该复选框。

static void DisableAllItemsExcept(CListCtrl& ctrl, int indexToKeepChecked)
{
    for (int nItem = 0; nItem < ctrl.GetItemCount(); nItem++)
        if (nItem != indexToKeepChecked)
            ctrl.SetCheck(nItem, FALSE);
}

//in begin message map
//ON_NOTIFY(LVN_ITEMCHANGED, IDC_SC_LIST, &CMyDlg::OnLvnItemchangedScList)

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

    //detect if we checked a new element
    if ( (pNMLV->uChanged & LVIF_STATE) && (pNMLV->uNewState & 0x2000) && (pNMLV->uOldState & 0x1000) )
        DisableAllItemsExcept(m_lcList, pNMLV->iItem);  

    *pResult = 0;
}

状态更改的检测基于以下文章:Get the notification code from Listview Control checkboxes。似乎没有在头文件中的任何位置定义值0x1000和0x2000,所以也许我正在使用未记录的功能。