EFanZh
EFanZh

Reputation: 2436

Can't expand TreeView item using TreeView_Expand

I want to make a TreeView's item dynamically create its children when the item is expanding. According to http://support.microsoft.com/kb/130697, I wrote the following code:

#define WIN32_LEAN_AND_MEAN

#include <Windows.h>
#include <windowsx.h>
#include <CommCtrl.h>

#pragma comment(lib, "ComCtl32.Lib")
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL MainWindow_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);
void MainWindow_OnDestroy(HWND hWnd);
void MainWindow_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify);
LRESULT MainWindow_OnNotify(HWND hWnd, int idFrom, NMHDR *pnmhdr);

#define ID_TREEVIEW 100
#define ID_EXPAND 101

HTREEITEM root_item;

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
    WNDCLASSEX wcex = { sizeof(wcex) };

    wcex.lpfnWndProc = WindowProc;
    wcex.hInstance = hInstance;
    LoadIconMetric(NULL, IDI_APPLICATION, LIM_LARGE, &wcex.hIcon);
    wcex.hCursor = static_cast<HCURSOR>(LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED));
    wcex.lpszClassName = TEXT("MainWindow");
    LoadIconMetric(NULL, IDI_APPLICATION, LIM_SMALL, &wcex.hIconSm);

    RegisterClassEx(&wcex);

    HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("TreeView Test"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL);
    ShowWindow(hWnd, nCmdShow);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        HANDLE_MSG(hWnd, WM_CREATE, MainWindow_OnCreate);
        HANDLE_MSG(hWnd, WM_DESTROY, MainWindow_OnDestroy);
        HANDLE_MSG(hWnd, WM_NOTIFY, MainWindow_OnNotify);
        HANDLE_MSG(hWnd, WM_COMMAND, MainWindow_OnCommand);
    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
}

BOOL MainWindow_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
{
    HWND h_treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, TEXT("TreeView"), WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_LINESATROOT, 10, 10, 400, 400, hWnd, reinterpret_cast<HMENU>(ID_TREEVIEW), GetModuleHandle(NULL), NULL);

    TVINSERTSTRUCT tvis = { TVI_ROOT, TVI_LAST, { TVIF_TEXT | TVIF_CHILDREN } };
    tvis.item.pszText = TEXT("Root");
    tvis.item.cChildren = 1;
    root_item = TreeView_InsertItem(h_treeview, &tvis);

    CreateWindow(WC_BUTTON, TEXT("Expand"), WS_CHILD | WS_VISIBLE, 420, 10, 75, 23, hWnd, reinterpret_cast<HMENU>(ID_EXPAND), GetModuleHandle(NULL), NULL);

    return TRUE;
}

void MainWindow_OnDestroy(HWND hWnd)
{
    PostQuitMessage(0);
}

LRESULT MainWindow_OnNotify(HWND hWnd, int idFrom, NMHDR *pnmhdr)
{
    switch (pnmhdr->code)
    {
    case TVN_ITEMEXPANDING:
        {
            LPNMTREEVIEW pnmtv = reinterpret_cast<LPNMTREEVIEW>(pnmhdr);
            if (pnmtv->action == TVE_EXPAND)
            {
                LPCTSTR items[] = { TEXT("Item 1"), TEXT("Item 2"), TEXT("Item 3") };
                for (LPCTSTR item : items)
                {
                    TVINSERTSTRUCT tvis = { pnmtv->itemNew.hItem, TVI_LAST, { TVIF_TEXT } };
                    tvis.item.pszText = const_cast<LPTSTR>(item);
                    TreeView_InsertItem(pnmhdr->hwndFrom, &tvis);
                }
            }
            break;
        }
    case TVN_ITEMEXPANDED:
        {
            LPNMTREEVIEW pnmtv = reinterpret_cast<LPNMTREEVIEW>(pnmhdr);
            if (pnmtv->action == TVE_COLLAPSE)
            {
                TreeView_Expand(pnmhdr->hwndFrom, pnmtv->itemNew.hItem, TVE_COLLAPSE | TVE_COLLAPSERESET);
            }
            break;
        }
    }
    return 0;
}

void MainWindow_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify)
{
    if (id == ID_EXPAND)
    {
        TreeView_Expand(GetDlgItem(hWnd, ID_TREEVIEW), root_item, TVE_EXPAND);
    }
}

The code creates a Window with a TreeView and a Button. When the button is clicked, the TreeView's root item should be expanded.

I can expand the root item for the first time. Either click the "Expand" button or click the "+" sign beside the root item is OK. But if I collapsed the root item, I can't expand it again by click the "Expand" button again, while the "+" sign still works. I noticed that although I've used the TVE_COLLAPSERESET flag to clear the TVIS_EXPANDEDONCE flag, it still has the flag before the TreeView_Expand call. Accroding to MSDN, I think I'll need the TVIS_EXPANDEDONCE flag to remain unset. Where am I doing it wrong?

Upvotes: 3

Views: 995

Answers (1)

EFanZh
EFanZh

Reputation: 2436

Use PostMessage to reset the TVE_COLLAPSERESET flag:

PostMessage(pnmhdr->hwndFrom, TVM_EXPAND, TVE_COLLAPSE | TVE_COLLAPSERESET, reinterpret_cast<LPARAM>(pnmtv->itemNew.hItem));

To my understanding, the TreeView send the TVN_ITEMEXPANDED notification, which send message back to the TreeView, which may cause the problem. We may need to delay the message until the current message has been processed.

Upvotes: 2

Related Questions