How to fix paint artifacts when replacing items in Tree View?

时间:2015-05-12 22:41:11

标签: winapi treeview comctl32

I have a Tree View positioned in the content area of a Tab Control (the Tree View is a sibling of the Tab Control). When I remove tree view items, add new tree view items, and select one of them, the tree view is not painted correctly; everything above the newly created+selected item is gray. Is there any way to make the tree view paint everything properly after removing and inserting items?

Before replacing items After replacing items

Observations:

  • If there are few enough tree view items that no scroll bar appears, then the tree view looks OK.
  • If there is no tab control adjacent to the tree view, then the tree view looks OK.
  • If the tree view is a child of the tab control, then the tree view looks OK (but the Tab key can’t navigate between the Tab Control and the Tree View using GetNextDlgTabItem/IsDialogMessage).
  • If I don’t select any items in the Tree View after inserting new nodes, then the tree view looks OK.

When I insert items into the tree, I call TreeView_InsertItem followed by TreeView_SelectItem. Full sample gist. In the sample program, the Ctrl+R accelerator replaces all the tree nodes and causes the artifacts.

1 个答案:

答案 0 :(得分:1)

这里有错误:

ACCEL accel[1]***; //change to accel[2]
accel[0].fVirt = FCONTROL | FVIRTKEY;
accel[0].key = 'R';
accel[0].cmd = IDM_REGENERATETREE;
accel[1].fVirt = FCONTROL | FVIRTKEY;
accel[1].key = 'S';
accel[1].cmd = IDM_SELECTRANDOM;
HACCEL haccel = CreateAcceleratorTable(accel, 2);

尝试保存先前的项目状态时会导致显示问题。如果您从previousStates删除addTreeItem,则不会出现显示问题。要正确保存状态,您可能需要std :: map和每个树项的一些用户标识。至少你应该使用std :: vector来更容易理解。

为了获得更好的视觉效果,您可以将TVS_LINESATROOT添加到TreeView,将WS_CLIPCHILDREN添加到主窗口。

编辑:

不应在addTreeItem中保存以前的项目状态。例如,刚刚插入的新项目还没有子项,因此无法展开。简化addTreeItem如下:

HTREEITEM addTreeItem(HWND htree, HTREEITEM par, HTREEITEM after, LPCTSTR str, LPARAM lp) 
{
    TVINSERTSTRUCT tvins;
    tvins.hParent = par;
    tvins.hInsertAfter = after;
    tvins.itemex.mask = TVIF_TEXT | TVIF_PARAM;
    tvins.itemex.pszText = const_cast<LPTSTR>(str);
    tvins.itemex.lParam = lp;
    HTREEITEM node = TreeView_InsertItem(htree, &tvins);
    return node; 
}

要保存上一个项目状态,每个项目应具有不同的ID。正如在这个示例中项目的名称对于每个节点而言是不同的,我们可以将其用于地图。但如果这是一个目录结构,它将无法工作,我们必须使用fullpath而不是节点名称。

void RootWindow::RegenerateTree()
{
    if (!m_hwndTreeView) return;
    if (!IsWindow(m_hwndTreeView)) return;
    HWND hwnd = m_hwndTreeView;

    //this will stop treeview from updating after every insert
    SetWindowRedraw(hwnd, 0);

    std::map<std::wstring, UINT> state;
    const int maxtext = 260;
    wchar_t buf[maxtext];
    std::wstring selection;

    UINT count = TreeView_GetCount(hwnd);
    if (count)
    {
        for (HTREEITEM item = TreeView_GetRoot(hwnd); item; item = nextItem(hwnd, item))
        {
            TVITEM tv{ 0 };
            tv.mask = TVIF_TEXT | TVIF_STATE;
            tv.stateMask = TVIF_TEXT | TVIF_STATE;
            tv.cchTextMax = maxtext;
            tv.pszText = buf;
            tv.hItem = item;
            if (TreeView_GetItem(hwnd, &tv))
                state[buf] = TreeView_GetItemState(hwnd, item, 
                TVIS_SELECTED | TVIS_EXPANDED);
        }
    }
    TreeView_DeleteAllItems(hwnd);

    addTreeItem...
    addTreeItem...
    addTreeItem...

    //restore previous item state here:
    if (count)
    {
        for (HTREEITEM item = TreeView_GetRoot(hwnd); item; item = nextItem(hwnd, item))
        {
            TVITEM tvitem{ 0 };
            tvitem.hItem = item;
            tvitem.mask = TVIF_TEXT;
            tvitem.cchTextMax = maxtext;
            tvitem.pszText = buf;
            if (TreeView_GetItem(hwnd, &tvitem))
                TreeView_SetItemState(hwnd, item, state[buf], 
                TVIS_SELECTED | TVIS_EXPANDED);
        }
    }

    SetWindowRedraw(hwnd, 1);
}