I need to implement the following scenario:
User expands a node, e.g. Node 1
;
User expands another node, e.g. Node 2
;
Collapse previous node ( Node 1
);
To visually explain what I mean, we shall use the following example image :
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 :
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.
I can not think of an algorithm for using the above messages to solve my task.
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;
}