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

来源:互联网 发布:手机端淘宝详情页制作 编辑:程序博客网 时间:2024/05/16 17:31
  • 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个函数


原创粉丝点击