Мне нужно реализовать следующий сценарий:
Пользователь расширяет узел, например Node 1
;
Пользователь расширяет другой узел, например Node 2
;
Свернуть предыдущий узел ( 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_ITEMEXPANDING
а также TVN_ITEMEXPANDED
сообщения, которые могут быть полезны.
Я также нашел NM_CLICK
уведомление, которое кажется интересным, так как я могу использовать TVM_HITTEST
сообщение для проверки, был ли клик +/ — кнопка.
Также я нашел TVN_KEYDOWN
сообщение, которое поможет мне с расширением, когда пользователь нажимает стрелка влево или же правая стрелка ключи.
Я не могу придумать алгоритм использования вышеуказанных сообщений для решения моей задачи.
Можете ли вы предложить мне алгоритм для обработки вышеуказанных сообщений, чтобы я мог позвонить CollapseNode(..)
функция?
Что-то вроде этого:
Хиттест в NM_CLICK
а затем вызвать вашу функцию или же Сохраните последний развернутый элемент в переменной и сверните его в ответ на TVN_ITEMEXPANDED
было бы хорошо для начала.
Спасибо.
Не совсем уверен, что это то, что вы после. Из замечаний, сделанных в комментариях, я думаю, что спецификации диктуют, что и фоны, и сборки должны быть в состоянии оставаться открытыми одновременно, хотя вопрос, по-видимому, указывает на то, что это должна быть или / или ситуация.
В любом случае, посмотрите на это.
По сути, когда я определяю, что узел расширен, я нахожу его предка, который является дочерним по отношению к корневому узлу. Если это корневой узел или один из его дочерних узлов, я ничего не делаю. В противном случае, я звоню вашему CollapseNode
функция на всех братьев и сестер расширенного узла.
(Я понимаю, что в моих комментариях что-то не так. Я был бы рад уточнить при необходимости)
Я, вероятно, также должен обратить ваше внимание на другое поведение, наблюдаемое при закрытии узла с расширенными дочерними объектами VS вручную, вызывающего CollapseNode на узле с расширенными дочерними элементами.
Наконец, вам придется проверить и при необходимости изменить контрольный идентификатор (IDC_TREEVIEW1
) дерева в onNotify
функция.
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;
}