TAG / mfc

遍历 CTreeCtrl

CTreeCtrl 的 GetNextItem 成员函数很诡异,nCode 设为 TVGN_NEXT | TVGN_CHILD 会一直返回传进去的 hItem 值,而不是返回下一个兄弟 item “或”第一个 child item。

所以,只好自己写遍历函数,没有采用递归的做法,用了一个 STL List 容器来保存下一个兄弟 item 和第一个 child item,遍历返回的依据是 item 的 lParam 值等于给定的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
HTREEITEM CModelNodeTreePane::_GetTreeItemByID(const unsigned int uiItemID)
{
    HTREEITEM   hCurrItem = m_kModelNodeTreeCtrl.GetRootItem(),
                hItem;
    std::list kItemList;
 
    kItemList.push_back(hCurrItem);
 
    while (kItemList.size() > 0)
    {
        hCurrItem = kItemList.front();
        kItemList.pop_front();
 
        if ((unsigned int)m_kModelNodeTreeCtrl.GetItemData(hCurrItem) == uiItemID)
        {
            return hCurrItem;
        }
 
        if ((hItem = m_kModelNodeTreeCtrl.GetChildItem(hCurrItem)) != NULL)
        {
            kItemList.push_back(hItem);
        }
        if ((hItem = m_kModelNodeTreeCtrl.GetNextSiblingItem(hCurrItem)) != NULL)
        {
            kItemList.push_back(hItem);
        }
    }
 
    return NULL;
}

动态创建的 CTreeCtrl 实例的消息响应

由于程序里的 CTreeCtrl 控件实例是通过 CTreeCtr::Create() 来创建的,无法通过 VS 的 Properties 面板里的 Control Events 工具来生成消息映射函数,但控件又需要响应鼠标点击事件,这时最简单的办法就是重载 CTreeCtr 实例的 owner 的 OnNotify() 成员虚函数(这个 owner 也必然是 CWnd 的子类):

protected:
    virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BOOL CTreePane::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    NMHDR* pNMHDR = (NMHDR*)lParam;
    ASSERT(pNMHDR != NULL);
 
    switch (pNMHDR->code)
    {
    case TVN_SELCHANGED:
        _OnTreeCtrlSelChanged(wParam, lParam, pResult);
        break;
 
    default:
        break;
    }
 
    return CWnd::OnNotify(wParam, lParam, pResult);
}