C++ 折叠treeview中的所有节点,最后一个展开的节点除外 导言和有关资料:

C++ 折叠treeview中的所有节点,最后一个展开的节点除外 导言和有关资料:,c++,winapi,collapse,treeviewitem,C++,Winapi,Collapse,Treeviewitem,我需要实现以下场景: 用户展开一个节点,例如节点1 用户扩展另一个节点,例如节点2 折叠上一个节点(节点1) 为了直观地解释我的意思,我们将使用以下示例图像: 现在,当用户单击组件1节点或其子节点组件时,我需要每隔一个节点折叠一次。下面的示例图片说明了这一点: 我实施此行为的努力: 通过浏览互联网,并稍微考虑一下我自己,我能够编写折叠节点及其子节点的帮助函数: void CollapseNode( HWND hTree, HTREEITEM hti ) { if( TreeView_

我需要实现以下场景:

  • 用户展开一个节点,例如
    节点1

  • 用户扩展另一个节点,例如
    节点2

  • 折叠上一个节点(
    节点1

  • 为了直观地解释我的意思,我们将使用以下示例图像:

    现在,当用户单击组件1节点或其子节点组件时,我需要每隔一个节点折叠一次。下面的示例图片说明了这一点:

    我实施此行为的努力: 通过浏览互联网,并稍微考虑一下我自己,我能够编写折叠节点及其子节点的帮助函数:

    void CollapseNode( HWND hTree, HTREEITEM hti )
    {
        if( TreeView_GetChild( hTree, hti ) != NULL )
        {
            TreeView_Expand( hTree, hti, TVE_COLLAPSE );
            hti = TreeView_GetChild( hTree, hti );
            do
            {
                CollapseNode( hTree, hti );
            }
            while( ( hti = TreeView_GetNextSibling( hTree, hti ) ) != NULL  );
        }
    }
    
    通过阅读MSDN文档,我发现
    TVN_itemsexpanding
    TVN_itemsexpanded
    消息可能有用

    我还发现
    NM_CLICK
    通知似乎很有趣,因为我可以使用
    TVM_HITTEST
    消息来测试单击是否在+/-按钮上

    我还发现了
    TVN_KEYDOWN
    消息,当用户按下左箭头键或右箭头键时,该消息将帮助我进行扩展

    问题: 我想不出使用上述消息来解决我的任务的算法

    问题: 你能给我建议一个处理上述消息的算法,这样我就可以调用我的
    CollapseNode(..)
    函数了吗

    大概是这样的:

    点击
    NM\u中的测试单击
    ,然后调用您的函数或将最后一个展开项存储在变量中,并折叠它以响应
    TVN\u itemsexpanded
    ,这将是一个好的开始


    谢谢。

    我不太确定这是你想要的。从评论中的评论来看,我认为规范规定背景和组件应该能够同时打开,尽管问题似乎表明应该是非此即彼的情况

    无论如何,看看这个

    基本上,当我确定某个节点已展开时,我会找到它的祖先,即根节点的子节点。如果它是根节点或根节点的子节点之一,我什么也不做。否则,我将对扩展节点的所有同级调用您的
    CollapseNode
    函数

    (我意识到我的评论是呃,有点欠缺。我很乐意根据需要澄清)

    我可能还应该提请您注意手动关闭具有扩展子节点的节点与在具有扩展子节点的节点上调用CollapseNode时观察到的不同行为

    最后,您必须检查并根据需要更改
    onNotify
    函数中树视图的控件ID(
    IDC_TREEVIEW1

    HTREEITEM getTopLevelParent(HWND treeWnd, TV_ITEM curItem)
    {
        HTREEITEM treeRoot, tmpItem;
        treeRoot = TreeView_GetRoot(treeWnd);
    
        tmpItem = curItem.hItem;
        if (tmpItem != treeRoot)
        {
            while (TreeView_GetParent(treeWnd, tmpItem) != treeRoot)
            {
                tmpItem = TreeView_GetParent(treeWnd, tmpItem);
            }
            /*
            TV_ITEM topLevelParent;
            wchar_t itemText[100];
            topLevelParent.hItem = tmpItem;
            topLevelParent.cchTextMax = 100;
            topLevelParent.pszText = itemText;
            topLevelParent.mask = TVIF_TEXT;
            TreeView_GetItem(treeWnd, &topLevelParent);
            wprintf(L"TopLevelParent (rootChild) Text: %s\n", itemText);
            */
            return tmpItem;
        }
        return NULL;
    }
    
    void CollapseNode( HWND hTree, HTREEITEM hti )
    {
        if( TreeView_GetChild( hTree, hti ) != NULL )
        {
            TreeView_Expand( hTree, hti, TVE_COLLAPSE );
            hti = TreeView_GetChild( hTree, hti );
            do
            {
                CollapseNode( hTree, hti );
            }
            while( ( hti = TreeView_GetNextSibling( hTree, hti ) ) != NULL  );
        }
    }
    
    void collapseAllChildrenExcept(HWND treeWnd, HTREEITEM parent, HTREEITEM dontClose)
    {
        HTREEITEM curNode;
    
        curNode = TreeView_GetChild(treeWnd, parent);
        if (curNode != NULL)
        {
            if (curNode != dontClose)
                    CollapseNode(treeWnd, curNode);
    
            while ((curNode = TreeView_GetNextSibling(treeWnd, curNode)) != NULL)
            {
                if (curNode != dontClose)
                    CollapseNode(treeWnd, curNode);
            }
        }
    }
    
    
    void onNotify(HWND hwnd, WPARAM wParam, LPARAM lParam)
    {
        if (wParam == IDC_TREEVIEW1)
        {
            LPNMHDR nmHdr = (LPNMHDR) lParam;
    
            if (nmHdr->code == TVN_ITEMEXPANDED)
            {
                NM_TREEVIEW FAR *pnmtv = (NM_TREEVIEW FAR *) lParam;
                if (pnmtv->action == TVE_COLLAPSE)
                    printf("TVE_COLLAPSE:\n");
                else if (pnmtv->action == TVE_EXPAND)
                {
                    printf("TVE_EXPAND: ");
    
                    HWND treeWnd = nmHdr->hwndFrom;
                    TV_ITEM curItem = pnmtv->itemNew;
    
    /*
                    curItem.mask = TVIF_TEXT;
                    curItem.cchTextMax = 100;
                    wchar_t itemText[100];
                    curItem.pszText = itemText;
                    TreeView_GetItem(treeWnd, &curItem);
                    wprintf(L"%s\n", curItem.pszText);
    */
    
                    HTREEITEM rootChild = getTopLevelParent(treeWnd, curItem);
                    if (rootChild != NULL)
                    {
    //                    printf("Need to close other nodes\n");
                        HTREEITEM parent, dontCloseMe;
                        parent = TreeView_GetParent(treeWnd, curItem.hItem);
                        dontCloseMe = curItem.hItem;
                        collapseAllChildrenExcept(treeWnd, parent, dontCloseMe);
                    }
    
    //                else
    //                    printf("Node requires no action to other nodes.\n");
                }
            }
        }
    }
    
    
    //  This function is called by the Windows function DispatchMessage()
    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)                  // handle the messages
        {
            case WM_DESTROY:
                PostQuitMessage (0);       // send a WM_QUIT to the message queue
                break;
    
            case WM_NOTIFY:
                onNotify(hwnd, wParam, lParam);
                break;
    
            default:                      // for messages that we don't deal with
                return DefWindowProc (hwnd, message, wParam, lParam);
        }
        return 0;
    }
    

    +坚持100分!我昨天看到了你的CP消息,然后发现你已经解决了这个障碍,但却面临着ClearText+XP问题。我刚才真的不羡慕你!我有一个想法,尽管我误解了你的问题,需要重新思考。我会爬回树上,直到找到父节点是根的节点。然后我会关闭根的所有其他子项。我来看看我是否能写出一些代码。:)@enhzflep:我很害怕你“向我保释”,但当你被雇用的时候,责任总是接踵而至。自从我们上次“讲话”以来,我遇到了各种各样的问题,但成功地解决了它们,除了ClearType(我怀疑其中一个问题能否解决,因为创建透明树视图的算法是个问题。如果我可以使用自定义绘制透明节点,那么一切都会很好)。谢谢你的帮助!致以最良好的问候,很高兴再次听到你:)对不起。事实上,当不下雨的时候,就下倾盆大雨!嘿,我在想你的问题,而且,你似乎只希望一次打开TestProject的一个孙子(及其后代)。对吗?如果是这样的话,您是否可以在节点展开时(仅)关闭其他节点?我的意思是,除非树的所有节点都展开了,否则第一个图像是不可能的,是吗?要显示组件,您必须展开Assembly1、2或3,所以我想知道您是否只需要处理TVN_展开通知?@enhzflep:这只是一个示例图片。实际上,当创建树时,节点都会折叠。我想保存最后一个展开的节点句柄,当用户展开新节点时,只需折叠保存的句柄,然后将新节点存储到该变量中。我只是在实现这个方面有问题。。。总之,你的结论是正确的。啊!好吧,我想我已经找到了一个几乎可行的解决方案。晚饭后我会把它做完。我看了一下,看到了
    lParam
    member。我可以存储
    int
    bool
    值,该值可以指示项目是否应折叠,并用于更新
    TVN\u itemsexpanded
    上的
    lParam
    ,或
    NM\u单击
    。(我还没有测试你的代码,希望我能尽快测试)。听起来是个好主意。我也可以考虑设置LPARAM成员来保存一个指向包含其他数据的结构的指针,这应该提供了任何好处-它可以方便地访问与特定项相关的任何其他数据。例如,我通常用ListVIEW使用这种方法。如果你有任何进展,你会考虑用这种方法来编辑你的文章吗?当然。不过,我不确定到底什么是进展。如果我能确定什么是改进,或者如果你告诉我自己什么会使解决方案更好,我会在下面的回答中添加任何其他想法作为评论。它有一个小的视觉伪影,因为我的树是透明的,但这并不重要。我将试图说服我的雇主彻底放弃“单一扩展”,因为它效率低下(这是一个解决treeview在滚动时出现的视觉瑕疵的方法)