CTreeCtrl:实现子结点随父结点状态一致的方法

要求:

  • CTreeCtrl的节点带有checkbox 
  • 由上到下控件: 将一个节点被check后,其所有的子结点被check; 反之,uncheck,则所有子节点unchecked 
  • 由下到上控制: 所有子节点被check后,这些节点的父结点被check; 如果子节点由*全被选中*状态,取消掉一个,则父节点应该置为*uncheck*

图片说明:


代码实现:

void CClassTreeWnd::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
{
// TODO: 在此添加控件通知处理程序代码
CPoint pt;
::GetCursorPos(&pt);
UINT uFlags=0;
RECT rect;
GetWindowRect(&rect);
CPoint ptTest;
//作减法是为了?
ptTest.x = pt.x - rect.left ;
ptTest.y = pt.y - rect.top ;
HTREEITEM hItem = HitTest(ptTest,&uFlags);
if (hItem!=NULL)
{
if( uFlags & TVHT_ONITEMSTATEICON )
{
theFirstStartItem = false;
m_bSetCheckOrChecked = GetCheck(hItem);
//遍历,所有子结点都打setcheck
TreeTravesal(hItem);      
          // 检查父结点是否需要变化
      
//ChangePrtItemAcdByChild(hItem);
       ::PostMessage(this->m_hWnd, CHANGE_PARENT_ITEM_CHECK, NULL, NULL);

}
}
*pResult = 0;
}


父——子

// 遍历一个树的所有子结点,并将其设为选中状态
//by Xue 20.09 2011
void CClassTreeWnd::TreeTravesal(HTREEITEM hStartItem)
{
//得到其子结点
HTREEITEM hChildItem = this->GetChildItem(hStartItem);
//对其子结点进行遍历
while(hChildItem!=NULL)
{
if (m_bSetCheckOrChecked)
{
SetCheck(hChildItem,FALSE);
}
else
{
SetCheck(hChildItem);
}

TreeTravesal(hChildItem); //递归遍历孩子节点
//对其兄弟结点进行遍历
hChildItem =this->GetNextItem(hChildItem, TVGN_NEXT);
}
}

子——父

void CClassTreeWnd::ChangePrtItemAcdByChild( HTREEITEM hItemClicked )
{
// 得到父节点
HTREEITEM hParent = GetParentItem(hItemClicked);

if (NULL != hParent)// 如果父节点不为空
{
// 记录父结点的状态
bool bParentIsChecked = true;

// 检查父结点的所有子结点
HTREEITEM hChild = GetNextItem(hParent,TVGN_CHILD);
while(NULL != hChild)
{
if (!GetCheck(hChild))// 有任一结点为 非选中状态,
{ // 则父节点不满足全选条件,置为unchecked
bParentIsChecked = false;
break;// 跳出检查
}
// 下一个子结点
hChild = GetNextSiblingItem(hChild);
}

// 设置父结点的状态
SetCheck(hParent,bParentIsChecked);

// 检查上级节点
ChangePrtItemAcdByChild(hParent);
}
}

 

关于响应时机的说明

注意到对于“父——子”这种情况,可以直接调用上面的函数;但对“子——父”这种情况,直接调用的话,在单击的时候,结点的“check”或者“uncheck”状态还未发生改变,会判断错误。

比如,一个结点有3个子结点,已经有两个打上了勾,这时点击第三个。正确的响应是父结点也被打上勾,这是因为三个已经满了。

但是实际的情况却是:当三个全部打上勾,再点击取消一个的时候,会把父结点打上勾。这时因为判断的是点击前的状态,而不是点击后的状态。

很自然的想法是,如果MFC有AfterClick()事件的响应函数该多好。可惜没有。

所以需要:

1. 手动定义消息:CHANGE_PARENT_ITEM_CHECK

2. 在OnNMClick()函数里发送消息

3. 消息响应函数里调用上面的第2个函数




 

posted on 2011-09-21 18:04  LateStop  阅读(2414)  评论(0编辑  收藏  举报

导航