Reputation: 563
In a MFC dialog, I have used a CListCtrl with checkbox. I want to disable multi checkbox selection , so that user can only select a single checkbox at a time. What is the best way to achieve this.I have done this
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;
}
Somehow I think this method is not so proper and can be made better in other way. All suggesions are welcomed.
EDIT: UPDATE: after writing the code , everything is working but I am facing a new problem. calling SetCheck() function inside OnItemChanged message handler function, it is calling the same function again, creating a recursion.Thus selection change is somehow slow. How to avoid this.Please help. ????
Upvotes: 1
Views: 4645
Reputation: 833
I found a shorter solution. The idea is to detect if the user checked a checkbox. If it is the case, unchecked all the other checboxes, otherwise do nothing. Contrary to the answer by @egur, this solution does not check the checkbox upon selection of the row.
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;
}
The detection of state change was based on the following article : Get the notification code from Listview Control checkboxes. The values 0x1000 and 0x2000 do not seems to be defined anywhere in the header file, so maybe I'm using an undocumented feature.
Upvotes: 0
Reputation: 563
Finally I have solved it . Now the checkbox and selection works at par. selecting checkbox selects the row and vice versa and one selection is possible. the code:
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;
}
Upvotes: 3
Reputation: 7960
When creating the control make sure this style is used LVS_SINGLESEL
.
It is passed in the CreateEx/CreateEx function. Also available from the resource editor (if control is added through it).
Upvotes: 2