实现CTreeCtrl父子节点的联动选择
本文实现了下面的功能:
- 当选中父节点时, 其所有子节点全部选中.
- 当取消选中父节点时, 其所以子节点全部取消选中.
- 点击子节点时, 根据子节点与其兄弟节点的选中状态, 自动设置父节点的选中状态.
通过继承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
}
实现CTreeCtrl父子节点的联动选择的更多相关文章
- 7.Android开源项目WheelView的时间和地址联动选择对话框
		类似WheelView的时间和地址联动选择对话框在现在App经常看到,今天小结下. 主布局界面: <LinearLayout xmlns:android="http://schemas ... 
- Devexpress TreeList控件绑定显示父子节点对像
		今天一位同事咨询Devexpress TreeList控件绑定自动显示父子节点对像,但结果是不会显示带父子节点关系,而是将所有的节点作为父节点显示出来了,对像类的代码如下 public class I ... 
- [js开源组件开发]js手机联动选择地区仿ios 开源git
		js手机联动选择地区 前言:由于网上找到了一个mobiscrool,比较全,但是不开源,只能试用15天,正式版竟然要三千块钱,穷人只能自己动手,写了个只针对弹窗地区选择的. 本站点所有的资源均在git ... 
- [js开源组件开发]js手机联动选择日期 开源git
		js手机联动选择日期 这里在前面的<js手机联动选择地区>的基础上,改造数据源之后形成的一个日期的选择器,当然你可以使用之前的PC上模式的<日期控件>,它同时也支持手机端,ht ... 
- 4个好用的JS联动选择插件
		jQuery City Select 一个简单的jQuery省市联动插件,可以自定义JSON字典实现其他内容的联动选择菜单. PCAS省.市.地区联动选择JS封装类 PCAS可能是国内使用人数最多的J ... 
- JQUERY省、市、县城市联动选择
		JQUERY 插件开发——CITYLINKAGE(省.市.县城市联动选择) 第一部分:背景 开发源于需求,本次城市联动选择插件算是我写插件的一个特例吧,不是我目前工作需要些的,算是兴趣驱使吧.之前 ... 
- Android学习小Demo(21)ListView的联动选择
		在日常的App开发中,尤其是在开发生活服务的应用上,非常多时候,我们会须要联动地展现省市区的数据等,需求大概例如以下: 1)展现全部省份 2)当点击某省份的时候,在二级菜单上展现此省份以下所属的城市列 ... 
- ASP.NET MVC页面UI之联动下拉选择控件(省、市、县联动选择)
		地区选择操作在WEB应用中比较常见的操作,本文在.net mvc3下实现了省市县三级联动选择功能. 本文博客出处:http://www.kwstu.com/ArticleView/admin_2013 ... 
- easyui Tree模拟级联勾选cascadeCheck,节点选择,父节点自动选中,节点取消,父节点自动取消选择,节点选择,所有子节点全部选择,节点取消,所有子节点全部取消勾选
		最近项目中用到easyui tree,发现tree控件的cascadeCheck有些坑,不像miniui 的tree控件,级联勾选符合业务需求,所以就自己重新改写了onCheck事件,符合业务需求.网 ... 
随机推荐
- 【LeetCode题解】25_k个一组翻转链表(Reverse-Nodes-in-k-Group)
			目录 描述 解法一:迭代 思路 Java 实现 Python 实现 复杂度分析 解法二:递归(不满足空间复杂度) 思路 Java 实现 Python 实现 复杂度分析 更多 LeetCode 题解笔记 ... 
- 并发编程之 CountDown 源码分析
			前言 Doug Lea 大神在 JUC 包中为我们准备了大量的多线程工具,其中包括 CountDownLatch ,名为倒计时门栓,好像不太好理解.不过,今天的文章之后,我们就彻底理解了. 如何使用? ... 
- [转]magento性能优化的教程(非常详细)
			本文转自:https://www.sypopo.com/post/kMQE8dERoV/ 前面优化 mod_deflate模块,将text. css 和 javascript 先进行压缩再发送到浏览器 ... 
- .Net Core MVC实现自己的AllowAnonymous
			全局过滤,在Startup中ConfigureServices里面添加如下代码 services.AddMvc(options => { options.Filters.Add(typeof(M ... 
- 一个div中有一个a标签,点击a标签的时候不进入div的点击事件
			//阻止冒泡上层clickevent.stopPropagation(); 
- t3用户-角色-权限hibernate经典配置
			用户-角色-权限hibernate经典配置. 既然有人问起,我就写下说明吧.在文章中间的配置文件那里.权当回忆一下,也帮助更多人.这是以前学校时写的,没有注释.都是贴的代码笔记.看到的莫要见怪.欢迎学 ... 
- mysql数据库自动备份脚本
			#!/bin/bash #功能说明:本功能用于备份mysql数据库 #编写日期:2018/05/17 PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin ... 
- 【git1】git+gitHub+webStorm的使用
			Git与GitHub什么关系? git:版本控制工具/系统. github:是一个用git控制的项目托管平台.(同样,码云和Gogs也是基于git的项目托管平台.) ...此文接下来讲的是怎么搭配Gi ... 
- 乐字节-Java8新特性之方法引用
			上一篇小乐介绍了<Java8新特性-函数式接口>,大家可以点击回顾.这篇文章将接着介绍Java8新特性之方法引用. Java8 中引入方法引用新特性,用于简化应用对象方法的调用, 方法引用 ... 
- Java多线程--JDK并发包(1)
			Java多线程--JDK并发包(1) 之前介绍了synchronized关键字,它决定了额一个线程是否可以进入临界区:还有Object类的wait()和notify()方法,起到线程等待和唤醒作用.s ... 
