문제

INTRODUCTION AND RELEVANT INFORMATION:

I need to implement the following scenario:

  1. User expands a node, e.g. Node 1;

  2. User expands another node, e.g. Node 2;

  3. Collapse previous node ( Node 1 );

To visually explain what I mean, we shall use the following example image :

enter image description here

Now when user clicks on Assembly 1 node or its child node Components, I need to collapse every other node. An example picture below illustrates this :

enter image description here

MY EFFORTS TO IMPLEMENT THIS BEHAVIOR:

Browsing through Internet, and with a little thinking of my own, I was able to write helper function that collapses node and its children :

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  );
    }
}

Reading through MSDN documentation I found TVN_ITEMEXPANDING and TVN_ITEMEXPANDED messages that might be useful.

I have also found NM_CLICK notification that seems interesting since I can use TVM_HITTEST message to test if click was on +/- button.

Also I have found TVN_KEYDOWN message that will help me with expanding when user presses left arrow or right arrow keys.

PROBLEM:

I can not think of an algorithm for using the above messages to solve my task.

QUESTION:

Can you please suggest me an algorithm for handling the above messages so I can call my CollapseNode(..) function ?

Something like this:

Hittest in NM_CLICK and then call your function or Store the last expanded item in a variable and collapse it in response to TVN_ITEMEXPANDED would be good for a start.

Thank you.

도움이 되었습니까?

해결책

Not quite sure this is what you're after. From remarks made in the comments, I think the specs dictate that both Backgrounds and Assemblies should be able to remain open simultaneously, though the question seems to indicate that it should be an either/or situation.

In any case, have a look at this.

Basically, when I determine that a node is expanded, I find its ancestor that is a child of the root node. If it is the root node or one of the root-node's children, I do nothing. Otherwise, I call your CollapseNode function on all of the expanded-node's siblings.

(I realize my comments are er, somewhat lacking. I'd be happy to clarify as needed)

I should probably also draw your attention to the different behaviour observed when manually closing a node with expanded children VS calling CollapseNode on a node with expanded children.

Lastly, you'll have to examine and change as necessary the control ID (IDC_TREEVIEW1) of the tree-view in the onNotify function.

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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top