通过递归算法完成树的级联勾选的一般思路

本文探讨了在项目中遇到的带有复选框的树形结构加载缓慢的问题,并提出了解决方案。文中详细介绍了前后端如何实现级联勾选逻辑,包括初始化时的算法及勾选后的处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/

1.背景

在某个项目中,发现当tree上加上checkbox后,初始化该树时会特别慢。现场树上的节点最深有三层,节点个数总和有200多个。经排查,为该tree的参数中开启了级联勾选的参数,导致初始化该树时每初始化一个节点便进行了一次级联判断,如此效率过低导致。

现在将级联勾选去除后,涉及到如下几个问题需要解决:

初始化时,需要在前端或者后端完成级联勾选逻辑。

  1. 手动勾选树上节点后,需要进行级联判断。
  2. 点击父节点时,子节点需要全部选中。
  3. 点击子节点时,需要判断父节点以及父组节点是否均需选中。

2.初始化时的后台级联选择算法

在树的数据初始化从后端读取时,我们可以首先获取到各子节点的选中状态,全部获取后,对构造出来的树结构进行整体判断是否进行父节点的勾选。其递归算法大致如下:

 

3.初始化后前端的级联选择算法(只讨论勾选)

树有两种状态,一种是勾选,一种是去勾选。这里仅仅只考虑勾选的过程。去勾选过程的算法与勾选算法一样。

当树初始化完成后,勾选树上任意节点时,需要完成如下两个级联判断:

a.如果勾选的为父节点,其所有子节点均需被勾选上,并且其父节点也要做级联判断。

b.如果勾选的为普通子节点,其父节点要做级联判断。

统一起来就是,需要做两个级联算法,分别是向上方向判断,和向下方向判断:

             

3.1针对父节点的级联算法

 

3.2针对子节点的级联判断

 

4.注意

递归算法中,递归层级(level)参数是个非常重要的参数,在控制操作逻辑上非常有用。

 

       

                                   -----欢迎转载,但保留版权,请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/

                                                                                                       如果您觉得本文确实帮助了您,可以微信扫一扫,进行小额的打赏和鼓励,谢谢 ^_^

                                                  

#include "stdafx.h" #include "YourProject.h" #include "B.h" IMPLEMENT_DYNCREATE(CB, CFormView) BEGIN_MESSAGE_MAP(CB, CFormView) ON_NOTIFY(NM_CLICK, IDC_TREE1, &CB::OnNMClickTree) END_MESSAGE_MAP() CB::CB() : CFormView(CB::IDD) { } CB::~CB() { } void CB::DoDataExchange(CDataExchange* pDX) { CFormView::DoDataExchange(pDX); DDX_Control(pDX, IDC_TREE1, m_treeCtrl); } void CB::OnInitialUpdate() { CFormView::OnInitialUpdate(); // 设置置形控件样式 m_treeCtrl.ModifyStyle(0, TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_CHECKBOXES); // 创建示例结构 HTREEITEM hRoot1 = m_treeCtrl.InsertItem(_T("根节点1"), TVI_ROOT); HTREEITEM hRoot2 = m_treeCtrl.InsertItem(_T("根节点2"), TVI_ROOT); HTREEITEM hChild1_1 = m_treeCtrl.InsertItem(_T("子节点1-1"), hRoot1); HTREEITEM hChild1_2 = m_treeCtrl.InsertItem(_T("子节点1-2"), hRoot1); HTREEITEM hChild2_1 = m_treeCtrl.InsertItem(_T("子节点2-1"), hRoot2); HTREEITEM hChild2_2 = m_treeCtrl.InsertItem(_T("子节点2-2"), hRoot2); m_treeCtrl.InsertItem(_T("孙节点1-1-1"), hChild1_1); m_treeCtrl.InsertItem(_T("孙节点1-1-2"), hChild1_1); m_treeCtrl.InsertItem(_T("孙节点2-1-1"), hChild2_1); // 展开所有根节点 m_treeCtrl.Expand(hRoot1, TVE_EXPAND); m_treeCtrl.Expand(hRoot2, TVE_EXPAND); } int CB::GetItemLevel(HTREEITEM hItem) { int level = 0; while ((hItem = m_treeCtrl.GetParentItem(hItem)) != NULL) level++; return level; } void CB::OnNMClickTree(NMHDR* pNMHDR, LRESULT* pResult) { CPoint pt; GetCursorPos(&pt); m_treeCtrl.ScreenToClient(&pt); UINT flags = 0; HTREEITEM hItem = m_treeCtrl.HitTest(pt, &flags); if (hItem && (flags & TVHT_ONITEMSTATEICON)) { // 关键修复:正确切换节点状态 BOOL bChecked = m_treeCtrl.GetCheck(hItem); m_treeCtrl.SetCheck(hItem, bChecked); // 更新节点状态 HandleCheckBoxChange(hItem, !bChecked); // 传入新状态处理级联 } *pResult = 0; } void CB::HandleCheckBoxChange(HTREEITEM hItem, BOOL bChecked) { if (bChecked) { // 勾选操作:递归勾选所有父节点和子节点 CheckAllParents(hItem); CheckAllChildren(hItem, TRUE); } else { // 取消勾选操作:递归取消所有子节点,并检查兄弟节点状态 CheckAllChildren(hItem, FALSE); CheckSiblingsAndUpdateParent(hItem); } } void CB::CheckAllChildren(HTREEITEM hItem, BOOL bChecked) { HTREEITEM hChild = m_treeCtrl.GetChildItem(hItem); while (hChild) { m_treeCtrl.SetCheck(hChild, bChecked); CheckAllChildren(hChild, bChecked); hChild = m_treeCtrl.GetNextSiblingItem(hChild); } } void CB::CheckAllParents(HTREEITEM hItem) { HTREEITEM hParent = m_treeCtrl.GetParentItem(hItem); while (hParent) { // 优化:只有当父节点未勾选时才更新,避免重复操作 if (!m_treeCtrl.GetCheck(hParent)) { m_treeCtrl.SetCheck(hParent, TRUE); CheckAllParents(hParent); // 继续向上更新 } else { break; // 父节点已勾选,无需继续向上 } } } void CB::CheckSiblingsAndUpdateParent(HTREEITEM hItem) { HTREEITEM hParent = m_treeCtrl.GetParentItem(hItem); if (!hParent) return; BOOL allSiblingsUnchecked = TRUE; HTREEITEM hSibling = m_treeCtrl.GetChildItem(hParent); // 检查所有兄弟节点是否都未勾选 while (hSibling) { if (m_treeCtrl.GetCheck(hSibling)) { allSiblingsUnchecked = FALSE; break; } hSibling = m_treeCtrl.GetNextSiblingItem(hSibling); } // 如果所有兄弟节点都未勾选,则取消父节点 if (allSiblingsUnchecked && m_treeCtrl.GetCheck(hParent)) { m_treeCtrl.SetCheck(hParent, FALSE); CheckSiblingsAndUpdateParent(hParent); // 递归向上检查 }以上是我的原代码,目前可以实现点击勾选根节点、子节点可以全部勾选或取消勾选下联节点,点击勾选子孙节点可以自动勾选上联节点直至根节点,希望你保证我目前可以实现的效果的前提下,帮我修改原代码做到当子孙节点全部取消勾选状态下,上联节点会自动取消勾选状态(当孙节点全部取消勾选那么对应子节点也自动取消勾选,当子节点全部取消勾选状态那么根节点也自动取消勾选状态) }
07-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值