ListCtrl Checkbox多选问题
需求与目标:
标准的Listctrl是支持多选的,但这个多选只是把那些选中行变成高亮而已,我想要的效果是像svn提交代码的那个listctrl那样:
1.可以用鼠标框选,可以按ctrl或shift进行组合选择.这是listctrl本身支持的,只要设置对了风格就没问题,这个不是重点,跳过 :)
2.在选择了多行之后,点击鼠标或按下空格,能够把所有选中行都选上(呵呵,这就是我想要的)
??没用过这个功能??马上装一个svn,BitComet也可以....
实现:
其实就是把所有的选中行的check box状态都设为true-->SetCheck就可以啦,当然发消息也可以:发LVM_SETITEMSTATE消息对LVIS_STATEIMAGEMASK进行处理就行。其实都一样。
问题:是你会发现,鼠标点击和空格本身我们不用处理listctrl都会自己处理,并改变当前选中行的check box状态,所以我们自己处理的时候就要对当前选中行直接“特殊处理”。否则的话:你刚刚设为TRUE的状态,马上就被listctrl自己处理的消息给改回去了。。。
解决方法:
1.屏蔽listctrl自己的消息,全部我们自己处理(空格消息这么处理会很方便,鼠标消息因为要得到点击的是哪一行,这么干会辛苦点,能偷懒就偷懒嘛~~)
2.还用listctrl自己的消息,但是我们处理完之后要多做一次状态设置,把被系统改的状态再次改回来(BOOL改了3次和改了1次是一样的嘛+_+)
部分代码:
 
/**//**
* @Function : CDebeheListCtrl::OnNMClick
* @brief : 鼠标点击消息
* @param : pNMHDR
* @param : pResult
* @return :
* @remark :
*/
void CDebeheListCtrl::OnNMClick(NMHDR* pNMHDR, LRESULT* pResult)
...{
// 按下shift和ctrl时不马上选中
NMITEMACTIVATE* pNMItemActivate = (NMITEMACTIVATE*)pNMHDR;
if (pNMItemActivate->iItem == -1
   ||pNMItemActivate->uKeyFlags == LVKF_SHIFT
   || pNMItemActivate->uKeyFlags == LVKF_CONTROL)
...{
   TRACE0("OnNMClick no use ");
}
else if(pNMItemActivate->ptAction.x > 0
    && pNMItemActivate->ptAction.x < LISTCTRL_FIRST_COL_WIDTH)
...{
   if (IsInSelected(pNMItemActivate->iItem))
   ...{
    CheckAllSelectedItems(pNMItemActivate->iItem);
    this->SetCheck(pNMItemActivate->iItem, !GetCheck(pNMItemActivate->iItem));
   }
}
}
/**//**
* @Function : CheckAllSelectedItems
* @brief : 改变所有选中行的check box状态
* @param : nCurItem 当前选中的行
* @return :
* @remark : 当前选中行nCurItem是-1的时候,自己计算check box的状态
*     nCurItem不为-1是,就直接用nCurItem行的状态取反来设置
*     大家(所有选中行)的状态
*/
BOOL CDebeheListCtrl::CheckAllSelectedItems(int nCurItem)
...{
BOOL bCheck = FALSE;
if (nCurItem == -1)
   bCheck = CalcCheckState();
else
   bCheck = !GetCheck(nCurItem);
// 遍历选中行,设置状态
INT nItemIndex = -1;  
nItemIndex = this->GetNextItem(-1, LVNI_SELECTED);
if(nItemIndex != -1)  
...{
   SetCheck(nItemIndex, bCheck);
}
while(TRUE)  
...{  
   nItemIndex = this->GetNextItem(nItemIndex, LVNI_SELECTED);  
   if(nItemIndex != -1)  
   ...{
    SetCheck(nItemIndex, bCheck);
   }
   else  
    break;  
}
return TRUE;
}
/**//**
* @Function : IsInSelected
* @brief : 判断nItem行,是否在选中行中
* @return : BOOL
* @param : int nItem 要判断的行
* @remark :
*/
BOOL CDebeheListCtrl::IsInSelected(int nItem)
...{
// 得到当前选中行的,另一种方式,Copy from msdn
BOOL bRet = FALSE;
POSITION   pos   =   this->GetFirstSelectedItemPosition();  
if   (pos   ==   NULL)  
   TRACE0("No   items   were   selected! ");  
else  
...{  
   while (pos)  
   ...{  
    int   nNextItem   =   this->GetNextSelectedItem(pos);  
    if (nItem == nNextItem)
    ...{
     bRet = TRUE;
     goto Exit0;
    }
   }  
}  
Exit0:
return bRet;
}
/**//**
* @Function : CalcCheckState
* @brief : 如果当前选中行是-1,计算一下对选中行要设置的状态
* @return : BOOL
* @remark : 除非所有的选中行状态都是TRUE,否则返回FALSE
*/
BOOL CDebeheListCtrl::CalcCheckState()
...{
BOOL bRet = FALSE;
BOOL bHavenChecked = FALSE;
INT nItemIndex = -1;  
nItemIndex = this->GetNextItem(-1, LVNI_SELECTED);
if(nItemIndex != -1)  
...{
   if(GetCheck(nItemIndex))
   ...{
    bHavenChecked = TRUE;
    goto Exit0;
   }
}
while(TRUE)  
...{  
   nItemIndex = this->GetNextItem(nItemIndex, LVNI_SELECTED);  
   if(nItemIndex != -1)  
   ...{
    if (GetCheck(nItemIndex))
    ...{
     bHavenChecked = TRUE;
     goto Exit0;
    }
   }
   else  
    break;  
}
Exit0:
bRet = !bHavenChecked;
return bRet;
}
BOOL CDebeheListCtrl::PreTranslateMessage(MSG* pMsg)
...{
// 之所以处理这三个消息是我试出来的 +_+
// 不把他们截下来的话,listctrl默认的空格处理,
// 会让我们的逻辑变得复杂
if (pMsg->message == WM_CHAR
   || pMsg->message == WM_KEYDOWN
   || pMsg->message == WM_KEYUP)
...{
   if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_SPACE)
   ...{
    int nSelected = this->GetSelectedCount();
    if (nSelected > 0)
    ...{
     int item = this->GetSelectionMark();
     if (IsInSelected(item))
     ...{
      CheckAllSelectedItems(item);
     }
     else
     ...{
      CheckAllSelectedItems(-1);
     }
    }
   }
// 把双击消息屏蔽,不然点的太快会感觉像是bug
if (pMsg->message == WM_LBUTTONDBLCLK)
   return TRUE;
   return TRUE; // 切记return TRUE,不然默认的空格处理就会起作用
}
return CListCtrl::PreTranslateMessage(pMsg);
}