Windows编程基础--第12节 MFC之树控件CTreeCtrl

2023-11-09

树形控件(Tree Control):用来显示一系列项目的层次关系,最典型的例子是显示磁盘上的文件与文件夹。如果有子项目的话,单击树形控件中的项目可以展开或者收缩其子项目。MFC提供了CTreeCtrl类进行支持。

树控件在windows程序中使用相对也是比较多的控件,例如windows资源管理器左边的浏览视图就是个树控件视图:
这里写图片描述
今天我们学习一下使用树控件,实现“添加节点”、“删除节点”、“修改节点”;

1. 新建项目

新建一个基于对话框项目“day14”,布局如下,自行处理:
这里写图片描述

控件名 控件ID
Tree Control IDC_TREE_VIEW
编辑框 IDC_EDIT_ADD_MDY
添加节点 IDC_BUTTON_ADD
删除节点 IDC_BUTTON_DEL
修改节点 IDC_BUTTON_MDY

2. 设置树控件属性

编辑树控件如下属性:

属性 修改值 解释
Has Buttons TRUE 在父节点旁边显示+或-
Has Lines TRUE 在父节点和子节点之间划线
Line At root TRUE 在根节点上划线

3. 添加树控件变量

给树控件添加一个变量“m_tree”,如图:
这里写图片描述

4. 实现添加节点按钮函数

双击“添加节点”按钮,编辑按钮点击方法:

void Cday14Dlg::OnBnClickedButtonAdd()
{
    // TODO: 在此添加控件通知处理程序代码
    CString strText;
    GetDlgItemText(IDC_EDIT_ADD_MDY,strText);
    if (strText.GetLength() == 0)
    {
        AfxMessageBox(_T("请输入节点名!"));
        return;
    }
    HTREEITEM hItem = m_tree.GetSelectedItem();
    if(hItem == NULL){
        hItem = TVI_ROOT;
    }

    TVINSERTSTRUCT ts ={0};
    ts.hParent = hItem;
    ts.hInsertAfter = TVI_LAST;
    ts.item.pszText = strText.GetBuffer();
    ts.item.mask = TVIF_TEXT;
    HTREEITEM hNewItem = m_tree.InsertItem(&ts);
    m_tree.SelectItem(hNewItem);
    m_tree.EnsureVisible(hNewItem);
}

代码解释:

  1. 先获取文本框中的值,如果值为空,则提示用户输入;
  2. 获取当前选中的节点,若不存在选中的节点,则获取根节点;
  3. 创建节点结构体,设置其父节点,插入方式,节点内容,内容可见等;
  4. 插入新的节点,并且设置节点保证可见;
    效果图:
    这里写图片描述

5. 实现删除节点按钮函数

双击“删除节点”按钮,编辑按钮点击方法:

void Cday14Dlg::OnBnClickedButtonDel()
{
    // TODO: 在此添加控件通知处理程序代码
    HTREEITEM hItem = m_tree.GetSelectedItem();
    if(hItem == NULL){
        AfxMessageBox(_T("请选择要删除的节点!"));
        return;
    }

    HTREEITEM hParentItem = m_tree.GetParentItem(hItem);
    m_tree.DeleteItem(hItem);
    m_tree.SelectItem(hParentItem);
}

代码解释:

  1. 获取选中的节点,若没有选中的节点,则提示用户;
  2. 获取选中节点的父节点
  3. 删除选中的节点
  4. 将其父节点设置为选中节点

删除前:
这里写图片描述
删除后:
这里写图片描述

6. 实现修改节点按钮函数

双击“修改节点”按钮,编辑按钮点击方法:

void Cday14Dlg::OnBnClickedButtonMdy()
{
    // TODO: 在此添加控件通知处理程序代码
    HTREEITEM hItem = m_tree.GetSelectedItem();
    if(hItem == NULL){
        AfxMessageBox(_T("请选择要修改的节点!"));
        return;
    }

    CString strText;
    GetDlgItemText(IDC_EDIT_ADD_MDY,strText);
    if (strText.GetLength() == 0)
    {
        AfxMessageBox(_T("请输入新的节点名!"));
        return;
    }
    m_tree.SetItemText(hItem,strText);
}

代码解释:

  1. 获取选中的节点,若没有选中的节点,则提示用户;
  2. 先获取文本框中的值,如果值为空,则提示用户输入;
    3.修改文本名

7. 映射节点选择消息

我们想实现在点击选择控件是,下方的文本框中自动出现选择节点的文本,那么就要映射节点的选择变化消息:TVN_SELCHANGE,如图添加事件处理函数:
这里写图片描述
编辑事件处理函数:

void Cday14Dlg::OnTvnSelchangedTreeView(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    // TODO: 在此添加控件通知处理程序代码

    HTREEITEM hItem = m_tree.GetSelectedItem();
    if(hItem != NULL){
        CString strText = m_tree.GetItemText(hItem);
        SetDlgItemText(IDC_EDIT_ADD_MDY, strText);
    }
    *pResult = 0;
}

代码解释:

  1. 获取选中的节点,若不存在,则略过,不处理;
  2. 获取选中的节点文本,将文本值设置到文本框中;

效果图:
这里写图片描述

至此,MFC树控件的简单使用已经结束,其实树控件还有很多属性,比如添加图标,这就到以后的实践中再来学习,附树控件常见属性及方法:

1、树形控件的属性

TVS_EDITLABELS:允许用户进行节点文本的编辑 TVS_HASBUTTONS:节点左侧添加一个按钮
TVS_HASLINES:父节点与子结点出现连线 TVS_LINESATROOT:子节点与根节点之间出现连线
TVS_NOTOOLTIPS:结点无动态提示 TVS_SINGLEEXPAND:节点的选中(未选中)t7展开(合拢)同步
MFC中以两种形式封装树形控件,一种是树形控件CTreeCtrl,另一种是树形视图控件CTreeView。对于一般的要求,比如在对话框中,使用CTreeCtrl比较方便。在使用树形视图控件时,只需要利用成员函数取得其引用,就可以像树形控件一样方便的使用:
CTreeCtrl& GetTreeCtrl( ) const;
调用InsertItem函数能够将节点插入树形控件中,并返回插入的项的HTREEITEM。树形控件的插入工作往往是在对话框的OnInitDialog函数中进行,而对于树形视图控件,则是在OnInitUpdate函数中进行。
树形控件中的节点数据可以是文本,也可以是图像。节点中使用的图像是和树形控件的图像列表相对应的。在树形控件中使用图像列表是通过使用SetImageList函数来完成。
树形控件能产生通告消息,如: TVN_BEGINDRAG开始拖拽 TVN_ITEMEXPANDED节点被展开或收缩
其消息映射使用WM_NOTIFI,如: WM_NOTIFI(TVN_BEGINDRAG,IDC_TREECTRL,OnBeginDrag);

2、树形控件TVN_BEGINRDRAG消息的响应

与树形控件有关的、常用的结构是TVITEM、TVINSERTSTRUCT 、NMTREEVIEW(NM_TREEVIEW
)。前两个是用于插入节点时使用,而NMTREEVIEW是与树形控件的通告消息相关的结构。
当用鼠标左键拖拽树形控件时,控件会发出TVN_BEGINDRAG通告消息;当用鼠标右键拖拽时,则会发出TVN_BEGINRDRAG通告消息。拖拽树形控件时需要使用到IImageList::BeginDrag函数:
IImageList::BeginDrag creates a temporary image list that is used for
dragging. In response to subsequent WM_MOUSEMOVE messages, you can
move the drag image by using IImageList::DragMove. To end the drag
operation, you can use IImageList::EndDrag.
通常使用CTreeCtrl::CreateDragImage函数创建一个被拖拽节点的图像并返回一个CImageList指针(注意被拖拽的数据节点必须包含图像,否则返回的CImageList指针为空),然后利用该指针来调用CImageList::BeginDrag函数。除此之外还需要调用CImageList::DragEnter函数锁定、更新窗口,并在指定的位置显示被拖拽的图像:
static BOOL PASCAL DragEnter( CWnd* pWndLock, CPoint point );
If pWndLock is NULL, this function draws the image in the display
context associated with the desktop window, and coordinates are
relative to the upper left corner of the screen.
注意BeginDrag函数只是在拖拽开始时创建要拖拽的图像,而DragEnter函数则显示该图像。
最后调用CWnd::SetCapture函数使后续所有的鼠标输入都发送到当前的CWnd对象而不管鼠标的位置(因为CImageList::DragEnter函数的第一个参数为NULL时表示在与桌面窗口相关的窗口,可以说就是当前程序的框架窗口,但不包括其他应用程序的窗口上显示被拖拽的图像。因此要使得被拖拽的图像在所有的窗口上而不仅仅是应用程序的框架窗口上显示,就需要调用CWnd::SetCapture函数。一旦调用了SetCapture函数,则在当前应用程序的非框架窗口,包括系统菜单上的鼠标按键动作均被发送到当前CWnd
对象,直到调用ReleaseCapture为止)。
然后就是在鼠标移动的消息响应函数中调用CImageList::DragMove函数移动被拖拽的图像,使之与鼠标的移动位置同步。最后是调用CImageList::DragShowNolock函数隐藏或显示拖拽的图像,但它并不是必须的,也可以不调用,因此之前已经调用过CImageList::DragEnter函数显示拖拽的图像了。当传递true值显示拖拽的图像时,DragShowNolock在这个过程中不像DragEnter函数一样锁定窗口。
值得注意的是,在拖拽节点过程中可以使用CTreeCtrl::HitTest函数判断鼠标滑动过程中所经过的点是否位于树形控件的某一个节点之上,如果是,则返回该树形节点的HTREEITEM。此时可以使用CTreeCtrl::SelectDropTarget函数或CTreeCtrl::SelectItem函数来高亮显示该节点。

3、树形控件的成员函数

InsertItem函数插入一个节点,并返回新插入的节点的HTREEITEM。
ItemHasChildren函数根据给定的HTREEITEM判断该节点是否存在子节点。
GetChildItem函数根据给定的HTREEITEM获取该节点下子节点的HTREEITEM,如果没有子节点,则返回NULL。
GetNextSiblingItem函数根据给定的HTREEITEM获取该节点的下一个同级节点。
EnsureVisible函数在必要的时候滚动视图列表控件使得其至少部分可见。需要注意的是,在使用TVE_COLLAPSE调用Expand收缩树形控件后不能再调用该函数,否则收缩操作将无效。

感伤:WE输了,RNG输了,心碎了。。。

项目源码可以访问我的码云

>>>我的私人博客<<<

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Windows编程基础--第12节 MFC之树控件CTreeCtrl 的相关文章

  • 向进度条添加百分比文本 C#

    我有一个方法可以显示进程栏何时正在执行以及何时成功完成 我工作得很好 但我想添加一个百分比 如果完成 则显示 100 如果卡在某个地方 则显示更少 我在网上做了一些研究 但我无法适应我正在寻找的解决方案 这是我的代码 private voi
  • InvalidOperationException - 对象当前正在其他地方使用 - 红十字

    我有一个 C 桌面应用程序 其中我连续创建的一个线程从源 实际上是一台数码相机 获取图像并将其放在 GUI 中的面板 panel Image img 上 这必须是另一个线程 如它是控件的代码隐藏 该应用程序可以工作 但在某些机器上 我会在随
  • 用于代数简化和求解的 C# 库 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 网络上有很多代数求解器和简化器 例如 algebra com 上不错的代数求解器和简化器 然而 我正在
  • 如何在 .NET Framework 2.0 中模拟“Func<(Of <(TResult>)>) 委托”?

    我尝试使用这个类代码项目文章 http www codeproject com KB threads AsyncVar aspx在 VB NET 和 NET Framework 2 0 中 除了这一行之外 所有内容似乎都可以编译Privat
  • 如何在c++中读取pcap文件来获取数据包信息?

    我想用 C 编写一个程序来读取 pcap 文件并获取数据包的信息 例如 len sourc ip flags 等 现在我找到了如下代码 我认为它会帮助我获取信息 但是我有一些疑问 首先我想知道应该将哪个库添加到我的程序中 然后什么是 pca
  • ClickOnce 应用程序错误:部署和应用程序没有匹配的安全区域

    我在 IE 中使用 FireFox 和 Chrome 的 ClickOnce 应用程序时遇到问题 它工作正常 异常的详细信息是 PLATFORM VERSION INFO Windows 6 1 7600 0 Win32NT Common
  • 当我们想要返回对象的引用时,为什么我们在赋值运算符中返回 *this 而通常(而不是 this)?

    我正在学习 C 和指针 我以为我理解了指针 直到我看到这个 一方面 asterix 运算符是解引用的 这意味着它返回值所指向的地址中的值 而与号 运算符则相反 它返回值存储的地址记忆 现在阅读有关赋值重载的内 容 它说 我们返回 this因
  • 复制目录内容

    我想将目录 tmp1 的内容复制到另一个目录 tmp2 tmp1 可能包含文件和其他目录 我想使用C C 复制tmp1的内容 包括模式 如果 tmp1 包含目录树 我想递归复制它们 最简单的解决方案是什么 我找到了一个解决方案来打开目录并读
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • 为什么调用非 const 成员函数而不是 const 成员函数?

    为了我的目的 我尝试包装一些类似于 Qt 共享数据指针的东西 经过测试 我发现当应该调用 const 函数时 会选择它的非 const 版本 我正在使用 C 0x 选项进行编译 这是一个最小的代码 struct Data int x con
  • 是否有实用的理由使用“if (0 == p)”而不是“if (!p)”?

    我倾向于使用逻辑非运算符来编写 if 语句 if p some code 我周围的一些人倾向于使用显式比较 因此代码如下所示 if FOO p some code 其中 FOO 是其中之一false FALSE 0 0 0 NULL etc
  • 我可以使用 moq Mock 来模拟类而不是接口吗?

    正在经历https github com Moq moq4 wiki Quickstart https github com Moq moq4 wiki Quickstart 我看到它 Mock 一个接口 我的遗留代码中有一个没有接口的类
  • Qt - ubuntu中的串口名称

    我在 Ubuntu 上查找串行端口名称时遇到问题 如您所知 为了在 Windows 上读取串口 我们可以使用以下代码 serial gt setPortName com3 但是当我在 Ubuntu 上编译这段代码时 我无法使用这段代码 se
  • CMake 无法确定目标的链接器语言

    首先 我查看了this https stackoverflow com questions 11801186 cmake unable to determine linker language with c发帖并找不到解决我的问题的方法 我
  • 如何在非控制台应用程序中查看 cout 输出?

    输出到调试窗口似乎相当繁琐 我在哪里可以找到cout如果我正在编写非控制台信息 则输出 Like double i a b cout lt lt b lt lt endl I want to check out whether b is z
  • 使用 %d 打印 unsigned long long

    为什么我打印以下内容时得到 1 unsigned long long int largestIntegerInC 18446744073709551615LL printf largestIntegerInC d n largestInte
  • 不同类型指针之间的减法[重复]

    这个问题在这里已经有答案了 我试图找到两个变量之间的内存距离 具体来说 我需要找到 char 数组和 int 之间的距离 char data 5 int a 0 printf p n p n data 5 a long int distan
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • 如何部署“SQL Server Express + EF”应用程序

    这是我第一次部署使用 SQL Server Express 数据库的应用程序 我首先使用实体 框架模型来联系数据库 我使用 Install Shield 创建了一个安装向导来安装应用程序 这些是我在目标计算机中安装应用程序所执行的步骤 安装
  • 如何将 PostgreSql 与 EntityFramework 6.0.2 集成? [复制]

    这个问题在这里已经有答案了 我收到以下错误 实体框架提供程序类型的 实例 成员 Npgsql NpgsqlServices Npgsql 版本 2 0 14 2 文化 中性 PublicKeyToken 5d8b90d52f46fda7 没

随机推荐

  • 变长参数表va_list,模板template,打造通用函数

    假设我想写一个支持变长参数的max函数 template
  • 还在找数据库日期如何格式化嘛?看这一篇文章就够了

    目录 1 MySQL数据库 2 PostgreSQL Oracle 人大金仓 1 MySQL数据库 DATE FORMAT 函数 示例 SELECT DATE FORMAT NOW Y m d H i S 食用方法如下 DATE FORMA
  • vue中nextTick使用引起的一个小问题

    问题描述 两个页面之间进行快速切换会报错元素找不到 问题原因 该方法的调用是在nextTick中 也就是放在微任务队列中 当切换到该页面后主任务开始执行 主任务执行完成后 更新dom元素 然后才是下一个tick 然后快速切出该页面 组件销毁
  • 量产国产服务器cpu芯片,中国第一!百度自研芯片量产:配国产CPU

    目前 三星电子与百度联合宣布 昆仑芯片已经完成所有研发工作 将在明年初投入规模量产 采用三星14nm工艺 这也是两家巨头的第一次代工合作 2018年7月份 百度发布了自主研发的中国首款云端AI全功能AI芯片 昆仑 号称业内设计算力最高的AI
  • 单片机没有串口,你会如何打印调试信息?

    摘要 输出调试信息是软件开发中必不可少的调试利器 在出现bug时如果没有调试信息将会是一件令人头痛的事 本文主要介绍在嵌入式开发中用来输出log的方法 这些方法都是在实际开发过程中使用过的 嵌入式开发的一个特点是很多时候没有操作系统 或者没
  • 【CVE-2021-3156】linux sudo提权复现及补丁修复

    前言 今天安全圈都在刷屏 CVE 2021 3156 这个漏洞 由于这是一个缓冲区溢出漏洞 通用性比较强 因此也跟风实验一下 详细复现及修复过程如下 一 实验环境 操作系统 kali linux 2020 1b 1 adduser命令新建一
  • 【深度学习】生成对抗网络

    下文以图片作为数据举例介绍 生成网络 生成器 gt 以假乱真 生成网络的职责是把随机点模仿成与真实数据集相似的图片 这些随机点是从一个潜在空间中随机抽取的 它可以看作一个实现 点对点变换 的映射 而真实图像在图像空间中的分布十分复杂 简单的
  • 破解windows7系统密码

    theme smartblue 一 利用5次shift漏洞破解win7密码 1 1 漏洞 1 在未登录系统时 连续按5次shift键 弹出程序c windows system32 sethc exe 2 部分win7及win10系统在未进入
  • MAX232无RS232电平信号输出、MAX232/3232硬件设计电路、电荷泵电容、max3221电路

    调试MAX3221芯片过程中 经测试 电路的单片机的uart可以正常收发数据 可无论单片机怎么发送数据 max3221都没有输出RS232电平的信号 看了论坛上的留言 正常的RS232电路中 芯片 V V 引脚电压应该为一倍多将近两倍的Vc
  • 银行家算法例题

    银行家算法 Banker s Algorithm 是一个避免死锁 Deadlock 的著名算法 是由艾兹格 迪杰斯特拉在1965年为T H E系统设计的一种避免死锁产生的算法 它以银行借贷系统的分配策略为基础 判断并保证系统的安全运行 目录
  • 【PTA】乙级 前世档案

    前世档案 C语言 题干 大致思路 题干 大致思路 n个问题 m个玩家 所以共有2的次方种结果 对应第一个图中的树状图 先考虑一个玩家的回答 用s统计最后结果的序号 先让结果数num除以2 如果为y 则不做处理 如果为n 则令s num 依次
  • short s1 = 1;s1 =s1+1;有错而short s1 = 1; s1+=1正确,为何?

    s1 s1 1和s1 1是有区别的 s1 s1 1会错因为s1是short 而1是int 所以运算时会自动转换为int 再将int赋值给s1是需要强制转换 所以会错 JAVA规范上说e1 e2 实际上是 e1 T1 e1 e2 其中T1是e
  • STM32读取编码器数据(STM32-1)

    编码器 encoder 是将信号或数据进行编制 转换为可用以通讯 传输和存储的信号形式的设备 按照外形可以分为实心轴和空心轴 按照工作原理编码器可分为增量式和绝对式两类 增量式编码器是将位移转换成周期性的电信号 再把这个电信号转变成计数脉冲
  • 补码的基础知识

    数在计算机中如何表示 举例来说 8在计算机中表示为二进制的1000 那么 8怎么表示呢 很容易想到 可以将一个二进制位 bit 专门规定为符号位 它等于0时就表示正数 等于1时就表示负数 比如 在8位机中 规定每个字节的最高位为符号位 那么
  • 使用TensorFlow实现LSTM

    使用TensorFlow实现LSTM 使用Cell实现 以Cell方式实现LSTM import os import numpy as np import tensorflow as tf from tensorflow import ke
  • Ubuntu 配置第三方动态库的系统环境变量

    环境 ubuntu16 04 Qt5 7 1 简述 将第三方动态库配置到系统环境变量中 便于使用 步骤 1 将第三方动态库的头文件及 so 文件拷贝到桌面 RSAInclude文件夹 lib文件夹 2 进入桌面文件夹 打开命令框 3 输入命
  • PHP上传Excel-xls-xlsx

    PHP上传Excel xls格式和xlsx格式 PHPExcle插件 引入插件 require once assets PHPExcel 1 8 Classes PHPExcel php 视图层代码
  • 安装TomCat的艰辛历程

    学习Java的初级阶段 是个充满无奈与呵呵的时间 就安装一个TomCat我就安装了一晚上 还没有成功 所以我给我的 捉猫泪史 写个传 以免后来者重蹈覆辙 首先 安装TomCat之前 先将Java环境搭好 就是下载并安装Java develo
  • Porter-Duff compositing rules

    1984 年 7 月 计算机图形 杂志 18 期 253 259 页 刊登了Porter Thomas 和 Duff Tom 的论文 Compositing Digital Ima ges 文中讲述了12个合成规则 这些规则都是基于一些简单
  • Windows编程基础--第12节 MFC之树控件CTreeCtrl

    树形控件 Tree Control 用来显示一系列项目的层次关系 最典型的例子是显示磁盘上的文件与文件夹 如果有子项目的话 单击树形控件中的项目可以展开或者收缩其子项目 MFC提供了CTreeCtrl类进行支持 树控件在windows程序中