欢迎光临
我们一直在努力

Windows学习笔记-11(MFC树控件Tree Control)

一、树控件概述

1.1 什么是树控件

树控件(Tree Control)是Windows通用控件之一,用于显示分层数据,以树形结构展示信息。在MFC中,树控件由CTreeCtrl类封装。

1.2 主要特点

  • 层次化显示数据

  • 支持展开/折叠节点

  • 支持图标和文本

  • 支持拖放操作

  • 支持多选(需要特定风格)

1.3 常见应用场景

  • 文件资源管理器目录结构

  • 组织结构图

  • 配置选项分类

  • 数据库导航

二、创建树控件

2.1 通过资源编辑器创建

  • 在对话框资源中添加Tree Control控件

  • 设置控件属性:

    • ID:如IDC_TREE1

    • Has buttons:显示展开/折叠按钮

    • Has lines:显示连接线

    • Lines at root:根节点连接线

    • Border:边框

    • Check boxes:复选框(可选)

  • 2.2 动态创建

    // 在对话框类中声明
    CTreeCtrl m_treeCtrl;

    // 在OnInitDialog()中创建
    BOOL CMyDialog::OnInitDialog()
    {
    CDialogEx::OnInitDialog();

    CRect rect(10, 10, 300, 400);
    m_treeCtrl.Create(WS_VISIBLE | WS_CHILD | WS_BORDER |
    TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS,
    rect, this, IDC_TREE1);

    return TRUE;
    }

    2.3 常用样式说明

    样式说明
    TVS_HASLINES 显示父子节点间的连线
    TVS_LINESATROOT 根节点显示连线
    TVS_HASBUTTONS 显示展开/折叠按钮
    TVS_EDITLABELS 允许编辑节点文本
    TVS_SHOWSELALWAYS 失去焦点时仍显示选择
    TVS_CHECKBOXES 显示复选框
    TVS_FULLROWSELECT 整行选择(XP风格)
    TVS_TRACKSELECT 跟踪选择
    TVS_DISABLEDRAGDROP 禁用拖放

    三、树控件的基本操作

    3.1 插入节点

    // 插入根节点
    HTREEITEM hRoot = m_treeCtrl.InsertItem(_T("根节点"), TVI_ROOT);

    // 插入子节点
    HTREEITEM hChild1 = m_treeCtrl.InsertItem(_T("子节点1"), hRoot);
    HTREEITEM hChild2 = m_treeCtrl.InsertItem(_T("子节点2"), hRoot);

    // 在指定位置插入
    HTREEITEM hChild3 = m_treeCtrl.InsertItem(_T("子节点3"), hRoot, TVI_LAST);
    HTREEITEM hChild4 = m_treeCtrl.InsertItem(_T("子节点4"), hRoot, TVI_FIRST);
    HTREEITEM hChild5 = m_treeCtrl.InsertItem(_T("子节点5"), hRoot, hChild2); // 插入到hChild2之前

    // 带图标的节点插入
    HTREEITEM hItemWithIcon = m_treeCtrl.InsertItem(_T("带图标的节点"),
    0, // 图标索引
    1, // 选中时的图标索引
    hRoot);

    3.2 设置节点数据

    每个树节点可以关联一个32位的自定义数据(DWORD_PTR):

    // 定义数据结构
    struct CMyItemData {
    int nID;
    CString strType;
    void* pData;
    };

    // 设置节点数据
    CMyItemData* pData = new CMyItemData;
    pData->nID = 1001;
    pData->strType = _T("Folder");
    pData->pData = NULL;

    m_treeCtrl.SetItemData(hItem, (DWORD_PTR)pData);

    // 获取节点数据
    CMyItemData* pRetrievedData = (CMyItemData*)m_treeCtrl.GetItemData(hItem);

    // 重要:在节点删除时需要清理内存

    3.3 节点操作

    // 获取选中节点
    HTREEITEM hSelected = m_treeCtrl.GetSelectedItem();

    // 获取父节点
    HTREEITEM hParent = m_treeCtrl.GetParentItem(hSelected);

    // 获取第一个子节点
    HTREEITEM hChild = m_treeCtrl.GetChildItem(hSelected);

    // 获取下一个兄弟节点
    HTREEITEM hNextSibling = m_treeCtrl.GetNextSiblingItem(hSelected);

    // 获取上一个兄弟节点
    HTREEITEM hPrevSibling = m_treeCtrl.GetPrevSiblingItem(hSelected);

    // 获取根节点
    HTREEITEM hRoot = m_treeCtrl.GetRootItem();

    // 展开/折叠节点
    m_treeCtrl.Expand(hItem, TVE_EXPAND); // 展开
    m_treeCtrl.Expand(hItem, TVE_COLLAPSE); // 折叠
    m_treeCtrl.Expand(hItem, TVE_TOGGLE); // 切换状态

    // 选择节点
    m_treeCtrl.SelectItem(hItem);
    m_treeCtrl.Select(hItem, TVGN_CARET);

    // 确保节点可见
    m_treeCtrl.EnsureVisible(hItem);

    // 删除节点
    m_treeCtrl.DeleteItem(hItem); // 删除单个节点
    m_treeCtrl.DeleteAllItems(); // 删除所有节点

    3.4 遍历树节点

    // 递归遍历所有节点
    void CMyDialog::TraverseTree(HTREEITEM hItem)
    {
    if (hItem == NULL) return;

    // 处理当前节点
    CString strText = m_treeCtrl.GetItemText(hItem);
    TRACE(_T("节点: %s\\n"), strText);

    // 递归遍历子节点
    HTREEITEM hChild = m_treeCtrl.GetChildItem(hItem);
    while (hChild != NULL)
    {
    TraverseTree(hChild);
    hChild = m_treeCtrl.GetNextSiblingItem(hChild);
    }
    }

    // 调用方法
    HTREEITEM hRoot = m_treeCtrl.GetRootItem();
    TraverseTree(hRoot);

    四、图标管理

    4.1 图像列表(CImageList)

    // 在对话框类中添加成员变量
    CImageList m_imageList;

    // 初始化图像列表
    BOOL CMyDialog::OnInitDialog()
    {
    // … 其他初始化

    // 创建图像列表
    m_imageList.Create(16, 16, ILC_COLOR32 | ILC_MASK, 4, 1);

    // 添加图标(从资源)
    HICON hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
    m_imageList.Add(hIcon);
    DestroyIcon(hIcon);

    hIcon = AfxGetApp()->LoadIcon(IDI_ICON2);
    m_imageList.Add(hIcon);
    DestroyIcon(hIcon);

    // 关联图像列表到树控件
    m_treeCtrl.SetImageList(&m_imageList, TVSIL_NORMAL);

    return TRUE;
    }

    // 插入带图标的节点
    HTREEITEM hItem = m_treeCtrl.InsertItem(_T("节点文本"),
    0, // 正常图标索引
    1, // 选中图标索引
    TVI_ROOT);

    4.2 动态设置图标

    // 设置节点图标
    m_treeCtrl.SetItemImage(hItem, 2, 2); // 设置正常和选中图标索引

    // 获取节点图标索引
    int nImage, nSelectedImage;
    m_treeCtrl.GetItemImage(hItem, nImage, nSelectedImage);

    五、复选框功能

    5.1 启用复选框

    // 创建时指定样式
    m_treeCtrl.Create(WS_VISIBLE | WS_CHILD | WS_BORDER |
    TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_CHECKBOXES,
    rect, this, IDC_TREE1);

    // 动态修改样式(不推荐,最好在创建时指定)
    LONG lStyle = GetWindowLong(m_treeCtrl.m_hWnd, GWL_STYLE);
    lStyle |= TVS_CHECKBOXES;
    SetWindowLong(m_treeCtrl.m_hWnd, GWL_STYLE, lStyle);

    5.2 复选框操作

    // 获取复选框状态
    UINT nState = m_treeCtrl.GetItemState(hItem, TVIS_STATEIMAGEMASK);
    BOOL bChecked = ((nState >> 12) – 1) == 1; // 1-未选,2-选中

    // 设置复选框状态
    m_treeCtrl.SetItemState(hItem, INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK); // 选中
    m_treeCtrl.SetItemState(hItem, INDEXTOSTATEIMAGEMASK(1), TVIS_STATEIMAGEMASK); // 未选

    六、编辑标签

    6.1 启用编辑功能

    // 创建时包含TVS_EDITLABELS样式
    m_treeCtrl.Create(WS_VISIBLE | WS_CHILD | WS_BORDER |
    TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_EDITLABELS,
    rect, this, IDC_TREE1);

    // 开始编辑
    m_treeCtrl.EditLabel(hItem);

    6.2 处理编辑事件

    在对话框的消息映射中添加:

    BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
    ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE1, OnTvnBeginlabeleditTree)
    ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE1, OnTvnEndlabeleditTree)
    END_MESSAGE_MAP()

    事件处理函数:

    void CMyDialog::OnTvnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
    {
    LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);

    // 可以在此处控制哪些节点可编辑
    if (/* 某些条件 */)
    {
    *pResult = 1; // 禁止编辑
    }
    else
    {
    *pResult = 0; // 允许编辑
    }
    }

    void CMyDialog::OnTvnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
    {
    LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);

    if (pTVDispInfo->item.pszText != NULL)
    {
    // 更新节点文本
    m_treeCtrl.SetItemText(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText);
    *pResult = 1; // 接受编辑
    }
    else
    {
    *pResult = 0; // 取消编辑
    }
    }

    七、拖放操作

    7.1 启用拖放

    // 创建时指定TVS_DISABLEDRAGDROP禁用,或不指定以启用

    // 处理拖放事件
    BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
    ON_NOTIFY(TVN_BEGINDRAG, IDC_TREE1, OnTvnBegindragTree)
    END_MESSAGE_MAP()

    7.2 实现拖放功能

    void CMyDialog::OnTvnBegindragTree(NMHDR* pNMHDR, LRESULT* pResult)
    {
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);

    // 设置拖放图像
    m_hDragItem = pNMTreeView->itemNew.hItem;

    // 创建拖放图像(使用节点文本)
    CString strText = m_treeCtrl.GetItemText(m_hDragItem);
    m_treeCtrl.CreateDragImage(m_hDragItem);

    // 开始拖放操作
    m_treeCtrl.BeginDrag(0); // 0表示使用默认光标

    // 设置捕获
    SetCapture();

    *pResult = 0;
    }

    // 在WM_MOUSEMOVE和WM_LBUTTONUP中处理拖放逻辑

    八、通知消息处理

    8.1 常用通知消息

    消息说明
    TVN_SELCHANGED 选择改变
    TVN_ITEMEXPANDED 节点展开/折叠
    TVN_BEGINLABELEDIT 开始标签编辑
    TVN_ENDLABELEDIT 结束标签编辑
    TVN_BEGINDRAG 开始拖放
    TVN_KEYDOWN 按键事件
    NM_CLICK 单击事件
    NM_DBLCLK 双击事件
    NM_RCLICK 右键单击

    8.2 消息处理示例

    // 消息映射
    BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
    ON_NOTIFY(TVN_SELCHANGED, IDC_TREE1, OnTvnSelchangedTree)
    ON_NOTIFY(NM_CLICK, IDC_TREE1, OnNMClickTree)
    ON_NOTIFY(NM_DBLCLK, IDC_TREE1, OnNMDblclkTree)
    ON_NOTIFY(NM_RCLICK, IDC_TREE1, OnNMRClickTree)
    END_MESSAGE_MAP()

    // 选择改变事件
    void CMyDialog::OnTvnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult)
    {
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);

    HTREEITEM hSelected = pNMTreeView->itemNew.hItem;
    if (hSelected != NULL)
    {
    CString strText = m_treeCtrl.GetItemText(hSelected);
    // 根据选择更新其他控件
    }

    *pResult = 0;
    }

    // 右键单击事件
    void CMyDialog::OnNMRClickTree(NMHDR* pNMHDR, LRESULT* pResult)
    {
    CPoint point;
    GetCursorPos(&point);
    m_treeCtrl.ScreenToClient(&point);

    HTREEITEM hItem = m_treeCtrl.HitTest(point, NULL);
    if (hItem != NULL)
    {
    m_treeCtrl.SelectItem(hItem);
    // 显示上下文菜单
    ShowContextMenu(hItem, point);
    }

    *pResult = 0;
    }

                                                                                                                                                                                                                                                                                  

     

    赞(0)
    未经允许不得转载:171主机测评 » Windows学习笔记-11(MFC树控件Tree Control)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址