对带复选框的树控件,有个常见的需求,就是当我们勾选了某个父节点时,如果其下有子节点,则所有子节点也应当处于勾选状态。相应地,某个子节点连同其所有兄弟节点被勾选时,相应地其父节点也应当自动被选中。
如此,当我们对树控件的某个节点进行复选操作时,需要处理两个递归逻辑:
1.判断其是否具有子节点,如果有,则将所有子节点都设置为相同的复选状态。子节点的子节点进入递归。
2.判断其同级所有兄弟节点状态是否相同,当且仅当所有兄弟节点连同被操作的节点均处于选中状态时,父节点设置为选中状态,否则设置为未其父节点未选中。父节点进入递归。
逻辑厘清了,就开始写代码。
首先,我们要获得被点击的节点Item:
void CDialogSdeLayer::OnNMClickTreeSdeLayer(NMHDR *pNMHDR, LRESULT *pResult)
{
NM_TREEVIEW* pHdr = (NM_TREEVIEW*)pNMHDR;
CPoint point;
UINT uFlag;
GetCursorPos(&point);
ScreenToClient(&point);
HTREEITEM hItem = m_treeLayerInfo.HitTest(point, &uFlag);
///处理点击操作
}
处理点击操作,先处理子级,再根据本级兄弟节点状态,决定是否处理父级:
void CDialogSdeLayer::SetCheckStatus(HTREEITEM hTreeItem, BOOL bCheck)
{
if (NULL == hTreeItem)
return;
SetChildCheckStatus(hTreeItem, bCheck);
HTREEITEM hParentItem = m_treeLayerInfo.GetParentItem(hTreeItem);
if (NULL == hParentItem)
return;
BOOL bChecked = TRUE;
HTREEITEM hFirstItem = m_treeLayerInfo.GetChildItem(hParentItem);
if (FALSE == bCheck)
bChecked = FALSE;
if (hFirstItem != hTreeItem && FALSE == m_treeLayerInfo.GetCheck(hFirstItem))
bChecked = FALSE;
if (TRUE == bChecked)
{
HTREEITEM hNextItem = NULL;
while ((hNextItem = m_treeLayerInfo.GetNextSiblingItem(hFirstItem)) != NULL)
{
if (hNextItem != hTreeItem && bCheck != m_treeLayerInfo.GetCheck(hNextItem))
{
bChecked = FALSE;
break;
}
hFirstItem = hNextItem;
}
}
m_treeLayerInfo.SetCheck(hParentItem, bChecked);
SetParentCheckStatus(m_treeLayerInfo.GetParentItem(hParentItem));
}
处理子级的递归方法如下:
void CDialogSdeLayer::SetChildCheckStatus(HTREEITEM hTreeItem, BOOL bCheck)
{
if (nullptr == hTreeItem)
return;
HTREEITEM hItem = hTreeItem;
if (TRUE == m_treeLayerInfo.ItemHasChildren(hItem))
{
HTREEITEM hChildItem = m_treeLayerInfo.GetChildItem(hItem);
m_treeLayerInfo.SetCheck(hChildItem, bCheck);
SetChildCheckStatus(hChildItem, bCheck);
HTREEITEM hSiblingItem = NULL;
while ((hSiblingItem = m_treeLayerInfo.GetNextSiblingItem(hChildItem)) != NULL)
{
m_treeLayerInfo.SetCheck(hSiblingItem, bCheck);
SetChildCheckStatus(hSiblingItem, bCheck);
hChildItem = hSiblingItem;
}
}
}
处理父级的递归方法如下:
void CDialogSdeLayer::SetParentCheckStatus(HTREEITEM hTreeItem)
{
if (nullptr == hTreeItem)
return;
BOOL bCheck = TRUE;
HTREEITEM hFirstItem = m_treeLayerInfo.GetChildItem(hTreeItem);
if (NULL == hFirstItem)
return;
if (FALSE == m_treeLayerInfo.GetCheck(hFirstItem))
bCheck = FALSE;
if (TRUE == bCheck)
{
HTREEITEM hNextItem = NULL;
while ((hNextItem = m_treeLayerInfo.GetNextSiblingItem(hFirstItem)) != NULL)
{
if (FALSE == m_treeLayerInfo.GetCheck(hNextItem))
{
bCheck = FALSE;
break;
}
hFirstItem = hNextItem;
}
}
m_treeLayerInfo.SetCheck(hTreeItem, bCheck);
SetParentCheckStatus(m_treeLayerInfo.GetParentItem(hTreeItem));
}
完毕。