WPF - перекрывающиеся пользовательские вкладки в TabControl и ZIndex - PullRequest
10 голосов
/ 26 марта 2010

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

Вид по умолчанию:
image

Удаление вкладок:
image

Добавление 2 или более вкладок в ряд:
image

Добавление более 1 вкладки за раз не приведет к сбросу 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.

Ответы [ 2 ]

5 голосов
/ 30 марта 2010

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

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

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

РЕДАКТИРОВАТЬ: Приведенная выше строка кода вызывала необычное мерцание при перетаскивании вкладок, которое кратко отображало вкладкупозади того, кого тащат.Я переместил код в отдельную функцию и вызвал ее с более низким приоритетом диспетчера, и это устранило проблему.Финальный код ниже:

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)));
                }
            }
        }
    }
1 голос
/ 26 марта 2010

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

...