【问题标题】:Can't expand TreeView item using TreeView_Expand无法使用 TreeView_Expand 展开 TreeView 项目
【发布时间】:2014-05-28 12:51:43
【问题描述】:

我想让 TreeView 的项目在项目扩展时动态创建其子项。根据http://support.microsoft.com/kb/130697,我写了如下代码:

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

代码创建了一个带有 TreeView 和 Button 的窗口。单击按钮时,应展开 TreeView 的根项。

我可以第一次展开根项目。单击“展开”按钮或单击根项目旁边的“+”号即可。但是如果我折叠了根项目,我无法通过再次单击“展开”按钮再次展开它,而“+”号仍然有效。我注意到虽然我使用了TVE_COLLAPSERESET 标志来清除TVIS_EXPANDEDONCE 标志,但它在TreeView_Expand 调用之前仍然有标志。根据MSDN,我想我需要TVIS_EXPANDEDONCE 标志保持未设置。我哪里做错了?

【问题讨论】:

  • 这听起来完全合乎逻辑。您使用了 TVE_COLLAPSERESET,所以当它再次展开时,您应该再次获得 TVIS_EXPANDEDONCE。
  • @HansPassant 我的意思是 之前 我在MainWindow_OnCommand 中调用TreeView_Expand,即使根项折叠时我重置了根项也已经具有TVIS_EXPANDEDONCE 标志,不知道什么时候定的。为了使我的代码正常工作,我必须在哪里进行更改?
  • TreeView 在生成通知时递归到其代码时会变得非常暴躁。嘎嘎喜欢这样的问题。通用的解决方案是等到以后,PostMessage() 自己可以做到这一点。
  • @HansPassant 对不起,“等到以后”​​是什么意思?我什么时候等?等什么?
  • 等到 Treeview 中的代码不再运行。也许this post 可以带来清晰,当然减去单线解决方案。 BeginInvoke 的等价物是 PostMessage。

标签: winapi treeview


【解决方案1】:

使用PostMessage 重置TVE_COLLAPSERESET 标志:

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

据我了解,TreeView 发送TVN_ITEMEXPANDED 通知,该通知将消息发送回TreeView,这可能会导致问题。我们可能需要延迟消息,直到当前消息处理完毕。

【讨论】:

    猜你喜欢
    • 2011-11-22
    • 1970-01-01
    • 1970-01-01
    • 2014-01-02
    • 2012-05-04
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 2013-10-07
    相关资源
    最近更新 更多