WPF - Перекрытие пользовательских вкладок в TabControl и ZIndex

Проблема
У меня есть пользовательский элемент управления вкладками, используя вкладки в виде Chrome, которые привязываются к ViewModel. Из-за формы края немного перекрываются. У меня есть функция, которая устанавливает tabItem ZIndex на TabControl_SelectionChanged, который отлично подходит для выбора вкладок и перетаскивания вкладок, однако, когда я добавляю или закрываю вкладку с помощью команды ретрансляции, я получаю необычные результаты. У кого-нибудь есть идеи?

Вид по умолчанию:
tabs_default.jpg

Удаление вкладок:
tabs_removing.jpg

Добавление двух или более вкладок в строке:
tabs_adding.jpg

Добавление более 1 вкладки за раз не будет reset zindex других недавно добавленных вкладок, поэтому они идут за вкладкой справа, а закрывающие вкладки неправильно отображают ZIndex из SelectedTab, который заменяет его и он отображается за вкладкой справа.

Код для установки ZIndex

private void PrimaryTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.Source is TabControl)
        {
            TabControl tabControl = sender as TabControl;
            ItemContainerGenerator icg = tabControl.ItemContainerGenerator;
            if (icg.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
            {
                foreach (object o in tabControl.Items)
                {
                    UIElement tabItem = icg.ContainerFromItem(o) as UIElement;
                    Panel.SetZIndex(tabItem, (o == tabControl.SelectedItem ? 100 :
                        90 - tabControl.Items.IndexOf(o)));
                }
            }
        }
    }

Используя точки останова, я вижу, что он правильно устанавливает ZIndex в то, что я хочу, однако макет не отображает изменения. Я знаю, что некоторые изменения действуют, потому что, если ни один из них не работает, края табуляции будут отменены (правые вкладки будут нарисованы поверх левых). Нажатие на вкладку правильно установит zindex всех вкладок (включая ту, которая должна быть нарисована сверху), и перетаскивание/удаление их для их изменения также корректно отображает (что удаляет и повторно вставляет элемент вкладки). Единственное отличие, о котором я могу думать, это использовать шаблон MVVM и кнопки, на которых вкладки "Добавить/Закрыть" являются командами ретрансляции.

Кто-нибудь знает, почему это происходит и как я могу это исправить?

p.s. Я попытался установить ZIndex в моем ViewModel и привязку к нему, однако то же самое происходит при добавлении/удалении вкладок с помощью команды relay.

Ответ 1

Спасибо, Эй, ваш второй комментарий привел меня к моему решению!

Я добавил tabItem.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate); для каждой итерации цикла.

Мне все равно было бы интересно узнать, сможет ли кто-нибудь найти способ обойти это без обновления каждого tabItem при каждом изменении. Я попытался обновить весь элемент управления вкладкой в ​​конце цикла, но это только работало для закрытия вкладок, а не их добавления. Я знаю, что Panel.ZIndex устанавливается правильно, он просто не выполняет это свойство при рендеринге.

EDIT: вышеприведенная строка кода вызывала необычное мерцание при перетаскивании/удалении вкладок, которые на короткое время отображали вкладку за тащиной. Я переместил код в отдельную функцию и назвал его более низким приоритетом диспетчера, и это устранило проблему. Окончательный код ниже:

private void PrimaryTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.Source is TabControl)
        {
            TabControl tabControl = sender as TabControl;

            tabControl.Dispatcher.BeginInvoke(
                new Action(() => UpdateZIndex(sender as TabControl)),
                DispatcherPriority.Background);
        }
    }

    private void UpdateZIndex(TabControl tabControl)
    {
        ItemContainerGenerator icg = tabControl.ItemContainerGenerator;

        if (icg.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
        {
            foreach (object o in tabControl.Items)
            {
                UIElement tabItem = icg.ContainerFromItem(o) as UIElement;
                if (tabItem != null)
                {
                    // Set ZIndex
                    Panel.SetZIndex(tabItem, (o == tabControl.SelectedItem ? 100 :
                        90 - tabControl.Items.IndexOf(o)));
                }
            }
        }
    }

Ответ 2

Похоже, вам просто нужно снова запустить свой алгоритм, когда коллекция изменится. Поскольку вы тестируете свойство ItemContainerGenerator.Status, алгоритм может не работать. Вы можете рассмотреть возможность прослушивания события StatusChanged, а когда он изменится на ContainersGenerated, снова запустите алгоритм.