zoukankan      html  css  js  c++  java
  • 实现CTreeCtrl父子节点的联动选择

    本文实现了下面的功能:

    1. 当选中父节点时, 其所有子节点全部选中.
    2. 当取消选中父节点时, 其所以子节点全部取消选中.
    3. 点击子节点时, 根据子节点与其兄弟节点的选中状态, 自动设置父节点的选中状态.

    通过继承CTreeCtrl实现, 代码如下(注释已经写得很清楚了):

    CustomTreeCtrl.h

    
    #pragma once
    
    
    // CCustomTreeCtrl
    
    class CCustomTreeCtrl : public CTreeCtrl
    {
    	DECLARE_DYNAMIC(CCustomTreeCtrl)
    
    public:
    	CCustomTreeCtrl();
    	virtual ~CCustomTreeCtrl();
    
    private:
    
        // 设置子结点选中状态
        void SetChildCheck(HTREEITEM hItem, BOOL bCheck);
    
        // 设置父结点选中状态
        void SetParentCheck(HTREEITEM hItem);
    
        // 自定义消息处理
        afx_msg LRESULT OnUserMsg(WPARAM wParam, LPARAM lParam);
    
    protected:
    	DECLARE_MESSAGE_MAP()
    public:
        afx_msg void OnNMClick(NMHDR *pNMHDR, LRESULT *pResult);
    };
    

    CustomTreeCtrl.cpp

    
    // CustomTreeCtrl.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include "CustomTreeCtrl.h"
    
    #define UM_CHANGE_PARENT_CHECK_STATUS (WM_USER+1011)
    
    // CCustomTreeCtrl
    
    IMPLEMENT_DYNAMIC(CCustomTreeCtrl, CTreeCtrl)
    
    CCustomTreeCtrl::CCustomTreeCtrl()
    {
    
    }
    
    CCustomTreeCtrl::~CCustomTreeCtrl()
    {
    }
    
    
    BEGIN_MESSAGE_MAP(CCustomTreeCtrl, CTreeCtrl)
        ON_NOTIFY_REFLECT(NM_CLICK, &CCustomTreeCtrl::OnNMClick)
        ON_MESSAGE(UM_CHANGE_PARENT_CHECK_STATUS, &CCustomTreeCtrl::OnUserMsg)
    END_MESSAGE_MAP()
    
    
    
    // CCustomTreeCtrl message handlers
    
    
    void CCustomTreeCtrl::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
    {
        // 获取点击坐标
        CPoint pt;
        ::GetCursorPos(&pt);
        ::ScreenToClient(m_hWnd, &pt);
    
        UINT nFlags = 0;
        HTREEITEM hItem = HitTest(pt, &nFlags);
        if (hItem)
        {
            // 选中当前结点
            this->SelectItem(hItem);
    
            // 如果是点击的CheckBox
            if (TVHT_ONITEMSTATEICON == (nFlags & TVHT_ONITEMSTATEICON))
            {
                // 获取当前选中状态(点击完成后的状态相反)
                BOOL bChecked = GetCheck(hItem);
    
                // 设置子结点选中状态
                SetChildCheck(hItem, !bChecked);
    
                // 设置父结点的状态
                // 由于是当前点击的状态,而不是点击后的状态, 还未更新, 所以通过消息来设置
                ::PostMessage(m_hWnd, UM_CHANGE_PARENT_CHECK_STATUS, (WPARAM)hItem, 0);
            }
        }
    
        *pResult = 0;
    }
    
    // 设置子结点选中状态
    void CCustomTreeCtrl::SetChildCheck(HTREEITEM hItem, BOOL bCheck)
    {
        // 得到子结点
        HTREEITEM hChildItem = GetChildItem(hItem);
        while (hChildItem != NULL)
        {
            // 设置子结点选中状态
            SetCheck(hChildItem, bCheck);
    
            // 递归遍历孩子节点
            SetChildCheck(hChildItem, bCheck);
    
            // 对兄弟结点进行遍历
            hChildItem = GetNextItem(hChildItem, TVGN_NEXT);
        }
    }
    
    // 设置父结点选中状态
    void CCustomTreeCtrl::SetParentCheck(HTREEITEM hItem)
    {
        // 得到父节点
        HTREEITEM hParent = GetParentItem(hItem);
        if (NULL != hParent)
        {
            // 记录父结点的状态     
            BOOL bParentIsChecked = TRUE;
    
            // 检查父结点的所有子结点
            HTREEITEM hChild = GetNextItem(hParent, TVGN_CHILD);
            while (hChild)
            {
                // 如有任一子结点为非选中状态, 父节点不满足全选条件,置为unchecked
                if (!GetCheck(hChild))
                {
                    bParentIsChecked = FALSE;
                    break;
                }
                hChild = GetNextSiblingItem(hChild);
            }
    
            // 设置父结点的状态
            SetCheck(hParent, bParentIsChecked);
    
            // 递归检查父节点
            SetParentCheck(hParent);
        }
    }
    
    // 自定义消息处理
    LRESULT CCustomTreeCtrl::OnUserMsg(WPARAM wParam, LPARAM lParam)
    {
        HTREEITEM hItem = (HTREEITEM)(wParam);
        if (hItem)
        {
            SetParentCheck(hItem);
        }
    
        return 0L;
    }
    
    

    使用例子

    
    // 定义
    CCustomTreeCtrl m_wndTreeCtrl;
    
    BOOL CTreeCtrlTest::OnInitDialog()
    {
        CDialogEx::OnInitDialog();
    
        CRect rc;
        GetClientRect(&rc);
    
        // 在父/子结点之间绘制连线, 在根/子结点之间绘制连线, 在失去焦点时也显示当前选中的结点, 状态选择框, 在每一个结点前添加一个按钮用于表示当前结点是否已被展开
        m_wndTreeCtrl.Create(WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | TVS_CHECKBOXES | TVS_HASBUTTONS, rc, this, 123333);
    
    
        for (size_t i = 0; i < 3; i++)
        {
            CString strRoot;
            if (0 == i)
            {
                strRoot = _T("AA");
            }
            else if (1 == i)
            {
                strRoot = _T("BB");
            }
            else
            {
                strRoot = _T("CC");
            }
    
            HTREEITEM hRoot = m_wndTreeCtrl.InsertItem(strRoot);
    
            for (int j = 0; j < 5; j++)
            {
                CString strChild;
                strChild.Format(_T("%s_%02d"), strRoot.GetBuffer(), j);
    
                HTREEITEM hChild = m_wndTreeCtrl.InsertItem(strChild, hRoot);
    
                for (int k = 200; k < 210; k++)
                {
                    CString strCSubChild;
                    strCSubChild.Format(_T("%s_%03d"), strChild.GetBuffer(), k);
    
                    HTREEITEM hSubChild = m_wndTreeCtrl.InsertItem(strCSubChild, hChild);
                }
            }
        }   
    
        return TRUE;  // return TRUE unless you set the focus to a control
                      // EXCEPTION: OCX Property Pages should return FALSE
    }
    
    
    
  • 相关阅读:
    卡片悬停效果制作
    一个简单的登录页
    登录oracle ORA-12541: TNS:no listener报错
    Tomcat启动超时问题Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds
    获取input type=radio属性的value值
    Mybatic逆向工程的使用
    Mysql下载与安装
    JQuery操作input
    Java集合list,map,set区别及遍历
    List<Map<String, Object>>取值
  • 原文地址:https://www.cnblogs.com/zxsh/p/9116554.html
Copyright © 2011-2022 走看看