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);
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值