【问题标题】:Collapse all nodes in treeview, except the last expanded one折叠树视图中的所有节点,除了最后一个展开的节点
【发布时间】:2014-07-03 09:04:42
【问题描述】:

简介及相关资料:

我需要实现以下场景:

  1. 用户展开一个节点,例如Node 1;

  2. 用户展开另一个节点,例如Node 2;

  3. 折叠上一个节点(Node 1);

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

现在当用户点击Assembly 1 节点或其子节点Components 时,我需要折叠所有其他节点。下面的示例图片说明了这一点:

我为实现此行为所做的努力:

浏览互联网,稍加思考,我能够编写折叠节点及其子节点的辅助函数:

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_ITEMEXPANDINGTVN_ITEMEXPANDED 消息可能有用。

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

我还发现TVN_KEYDOWN 消息可以帮助我在用户按下左箭头右箭头 键时进行扩展。

问题:

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

问题:

您能否建议我一种处理上述消息的算法,以便我可以调用我的CollapseNode(..) 函数?

类似这样的:

NM_CLICK 中进行测试,然后调用您的函数将最后展开的项目存储在变量中并折叠它以响应TVN_ITEMEXPANDED开始。

谢谢。

【问题讨论】:

  • 毅力+100分!我昨天看到了你的 CP 消息,然后发现你已经解决了这个障碍,只是遇到了 ClearText + XP 问题。我现在肯定不羡慕你!我有一个想法,虽然我误读了你的问题,需要重新思考。我只是爬回树上,直到找到其父节点为根的节点。然后我会关闭根的所有其他孩子。我会看看我是否不能敲出一些代码。 :)
  • @enhzflep:我害怕你“保释我”,但是当你被雇用时,责任又会不断出现。自从我们上次“发言”以来,我遇到了各种问题,但设法成功解决了这些问题,除了 ClearType (我怀疑一个会得到解决,因为创建透明树视图的算法是问题。如果我可以用自定义绘制透明地绘制节点,那么事情会没事的 )。感谢您尝试提供帮助!最好的问候,很高兴再次听到你的声音:)
  • 很抱歉。确实,不下雨的时候,就是倾盆大雨!嘿,我正在考虑您的问题,而且,您似乎只希望一次打开 TestProject 的一个孙子(及其后代)。那是对的吗?如果是这样,您不能在扩展节点时简单地关闭其他节点(仅)吗?我的意思是,除非在所有节点都展开的情况下创建树,否则第一个图像是不可能的,不是吗?要显示组件,您必须展开 Assembly1、2 或 3 - 所以我想知道您是否真的只需要处理 TVN_EXPANDED 通知?
  • @enhzflep:这只是一个示例图片。实际上,当创建的树节点全部折叠时。我想保存最后一个展开的节点句柄,当用户展开新节点以折叠保存的句柄然后将新节点存储到该变量中时。我只是在实现这一点时遇到了问题……总之,你的结论是正确的。
  • 啊!好吧,我想我有一个几乎可行的解决方案。我会在晚饭后完成它。

标签: c++ winapi collapse treeviewitem


【解决方案1】:

不太确定这是您所追求的。从 cmets 中的评论来看,我认为规范要求背景和程序集应该能够同时保持打开状态,尽管问题似乎表明它应该是一个非此即彼的情况。

无论如何,看看这个。

基本上,当我确定一个节点被展开时,我发现它的祖先是根节点的子节点。如果它是根节点或根节点的子节点之一,我什么也不做。否则,我会在扩展节点的所有兄弟节点上调用您的 CollapseNode 函数。

(我意识到我的 cmets 呃,有点缺乏。我很乐意根据需要澄清)

我可能还应该提请您注意在手动关闭具有扩展子节点的节点 VS 在具有扩展子节点的节点上调用 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;
}

【讨论】:

  • 我查看了TVITEM structure 并看到了lParam 成员。我可以存储intbool 值,这些值可以指示项目是否应该折叠,并使用TreeView_SetItemTVN_ITEMEXPANDEDNM_CLICK 上更新lParamIt seems I do not need pointers for that(我还没有测试你的代码,希望我会尽快测试)。
  • 对我来说听起来是个好主意。我也可以考虑将 lParam 成员设置为包含指向包含其他数据的结构的指针,如果这提供任何好处 - 它可以提供一种简单的方法来访问与特定项目相关的任何其他数据。例如,我通常在 ListViews 中使用这种方法。
  • 如果你取得任何进展,你会考虑用这种方法编辑你的帖子吗?
  • 是的,当然。我不确定究竟什么会被认为是进展。如果我能够确定什么构成改进,或者如果您自己告诉我什么会使解决方案变得更好,我会在下面的评论中添加任何其他想法。
  • 它有一个小的视觉伪影,因为我的树是透明的,但没关系。我会尝试说服我的雇主完全放弃“单一扩展”,因为它效率低下(这是滚动发生时透明树视图的视觉伪影的一种解决方法,但我已经解决了这个问题)。赞成并正式接受!最好的问候:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-29
  • 2013-07-01
  • 2020-04-22
  • 2013-08-17
  • 1970-01-01
  • 2012-03-21
  • 1970-01-01
相关资源
最近更新 更多