Как исправить артефакты рисования при замене элементов в Tree View?

У меня есть представление в виде дерева, расположенное в области содержимого элемента управления вкладками (представление в виде дерева является родственным элементом управления вкладками). Когда я удаляю элементы древовидного представления, добавляю новые элементы древовидного представления и выбираю один из них, древовидное представление отображается неправильно; все, что находится над вновь созданным + выбранным элементом, отображается серым цветом. Есть ли способ заставить дерево отображать все правильно после удаления и вставки элементов?

Перед заменой элементовПосле замены элементов

Наблюдения:

  • Если элементов древовидного представления недостаточно, чтобы не отображалась полоса прокрутки, древовидное представление выглядит нормально.
  • Если рядом с древовидным представлением нет элемента управления вкладками, то древовидное представление выглядит нормально.
  • Если древовидное представление является дочерним элементом элемента управления вкладками, тогда древовидное представление выглядит нормально (но клавиша Tab не может перемещаться между элементом управления вкладками и древовидным представлением с помощью GetNextDlgTabItem/IsDialogMessage).
  • Если я не выберу какие-либо элементы в представлении в виде дерева после вставки новых узлов, представление в виде дерева выглядит нормально.

Когда я вставляю элементы в дерево, я вызываю TreeView_InsertItem, а затем TreeView_SelectItem. Полная суть. В примере программы ускоритель Ctrl+R заменяет все узлы дерева и вызывает артефакты.


person yonran    schedule 12.05.2015    source источник
comment
Попробуйте установить WS_CLIPSIBLINGS/WS_CLIPCHILDREN на вкладке управления.   -  person Jonathan Potter    schedule 13.05.2015


Ответы (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; 
}

Чтобы сохранить предыдущее состояние элемента, каждый элемент должен иметь свой идентификатор. Поскольку в этом примере имя элемента отличается для каждого узла, мы можем использовать его для карты. Но если бы это была структура каталогов, она бы не работала, мы должны использовать полный путь вместо имени узла.

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);
}
person Barmak Shemirani    schedule 13.05.2015
comment
Спасибо! Добавление WS_CLIPCHILDREN в окно верхнего уровня, добавление WS_CLIPSIBLINGS в элемент управления вкладками и вызов SetWindowPos для перемещения древовидного представления поверх элемента управления вкладками в z-порядке устранили серые области. Теперь единственная проблема в том, что кнопки на корневом уровне исчезают, когда я заново создаю дерево. - person yonran; 13.05.2015
comment
Это устраняет только незначительное мерцание. Даже если вы удалите вкладку управления, проблема останется. Основная проблема с previousStates в addTreeItem, поэтому предметы пропадают. - person Barmak Shemirani; 13.05.2015
comment
Теперь я пересмотрел пример, чтобы использовать std::map вместо блока LocalAlloc. - person yonran; 13.05.2015
comment
Вы не используете здесь LPARAM или, возможно, собираетесь использовать его позже. Я изменил его на std::map<std::wstring, UINT> на данный момент. - person Barmak Shemirani; 13.05.2015
comment
На самом деле я использую LPARAM в качестве уникального идентификатора узла в этом примере. Но спасибо за всю помощь! Я больше не устанавливаю TVIS_EXPANDED при создании элемента дерева; Я жду, пока я добавлю детей. Я вызываю TreeView_Expand и TreeView_SelectItem вместо установки состояния TVIS_EXPANDED | TVIS_SELECTED. Артефакты рисования исчезли. А для уменьшения мерцания использую SetWindowRedraw. - person yonran; 14.05.2015
comment
Да, это должно хорошо работать. Вы также можете использовать TreeView_EnsureVisible в самом конце один раз, чтобы убедиться, что выделение не прокручивается за пределы поля зрения. - person Barmak Shemirani; 14.05.2015