Стили WPF Error Styles отображаются только на видимой вкладке элемента управления вкладкой

У меня есть объект данных, используемый для хранения моих данных пользовательского интерфейса, который поддерживает INotifyPropertyChanged и IDataErrorInfo. Первоначально у меня были все элементы управления пользовательского интерфейса, отображаемые в одном большом приложении WPF, и с радостью увидели ошибки, отмеченные этим пользовательским стилем:

    <!-- Set error style for textboxes -->
    <Style x:Key="txtBoxErrStyle" TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="ToolTip" 
                            Value="{Binding RelativeSource={x:Static RelativeSource.Self}, 
                            Path=(Validation.Errors)[0].ErrorContent}" />
            </Trigger>
        </Style.Triggers>

        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <DockPanel DockPanel.Dock="Right">
                        <AdornedElementPlaceholder />
                        <Image Source="Error.png"
                                   Height="16"
                                   Width="16"
                                   ToolTip="{Binding Path=AdornedElement.ToolTip, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}" />
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Я сегодня реорганизовал программу и решил распространять различные элементы пользовательского интерфейса на нескольких страницах TabControl. Структура структуры, которую я использую для этого:

<tabcontrol>
    <tabitem>
        <AdornerDecorator>
           [.. various Stack Panels, Groups and UI controls moved from original layout ..]
        </AdornerDecorator>
    </tabItem>
    <tabitem>
        <AdornerDecorator>
           [.. various Stack Panels, Groups and UI controls moved from original layout ..]
        </AdornerDecorator>
    </tabItem>

    ...
 </tabcontrol>

(Я использую AdornerDecorator, как я уже имел в предыдущей программе, стиль ошибки не перерисовывается при перестановке страниц вкладок. Я не могу вспомнить, где я это видел, но это помогло мне.)

Теперь, когда я запускаю свою программу, стиль ошибки корректно отображается на TabItem, который открывается при запуске программы, но не выполняет рендеринг на другом (скрытом) TabItem s. Когда я выбираю (и обнаруживаю) один из этих TabItem, устанавливается подсказка стиля ошибки, но изображение значка ошибки не отображается.

Я также протестировал удаление пользовательского стиля и вернусь обратно к стилю ошибки WPF по умолчанию для текстовых полей, и я по-прежнему получаю аналогичное поведение, т.е. красный блок вокруг элемента управления TabItem, который скрыт при открытии программы.

Итак, кажется, что я полностью пропускаю то, что останавливает стили ошибок от правильной рендеринга, отличные от открытой вкладки Item. Любые идеи?

Изменить сентябрь 3 Изменено описание, чтобы лучше понять, что я видел

Разговор о Дежавю в 2014 году

В ноябре 2014 года и сегодня у меня была эта глупая проблема WPF с шаблонами ошибок, которые не отображаются на элементах, представленных в контроллере табуляции. Что-то в глубине моего ума предполагает, что я видел эту проблему раньше. Поэтому я google, и первое, что появляется, это мой собственный вопрос с 2009 года.

На этот раз я вижу комментарий от dkl, который был добавлен после того, как я решил вещи в последний раз. Поэтому я попробовал это и использовал это решение (которое хорошо работало, и мне не нужно было посыпать элемент Adorner над элементами управления вкладками):

<Style x:Key="TextBoxErrorStyle" TargetType="TextBox">
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="Validation.HasError" Value="True" />
                <Condition Property="IsVisible" Value="True" />
            </MultiTrigger.Conditions>
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="True">
                            <TextBlock  DockPanel.Dock="Right" 
                                Foreground="Red"
                                FontSize="14pt" 
                                 Margin="-15,0,0,0" FontWeight="Bold">*
                            </TextBlock>
                            <Border BorderBrush="Red" BorderThickness="2">
                                <AdornedElementPlaceholder Name="controlWithError"/>
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ToolTip" 
                    Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}" />
        </MultiTrigger>
    </Style.Triggers>
</Style>

Ответы

Ответ 1

(Я использую AdornerDecorator как я испытали в предыдущей программе стиль ошибки не перерисовывается при обмене языковыми страницами. Я не могу помните, где я это видел, но это было помогите мне)

Предположительно, этот действительно важный совет исходит из Karl Shifflets blog, по крайней мере, он обращается к одной теме: Ошибки проверки WPF исчезают внутри TabControl при переключении TabItems.

Учитывая это, ваша проблема может быть просто связана, то есть вышеприведенный совет/код гарантирует, что для каждой вкладки есть выделенный AdornerLayer теперь, когда слой adorner родительского элемента отбрасывается при переключении вкладок. Этот выделенный слой adorner по-прежнему нуждается в особом лечении, хотя, например, вопрос WPF ErrorTemplate отображается, когда он не сфокусирован?, который в основном касается вашей проблемы с ног на голову. Следовательно, я предлагаю вам объединить и расширить выделенное решение для последнего с вашим стилем и попробовать следующий (непроверенный код на данный момент):

<Style x:Key="ErrorTemplate" TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">...</Trigger>
        <Trigger Property="IsVisible" Value="false">
            <Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
        </Trigger>
        <Trigger Property="IsVisible" Value="true">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>...</Setter.Value>
            </Setter>
        </Trigger>
    </Style.Triggers>
</Style>

См. мой комментарий относительно вашего обновления "Стиль ошибки радио кнопки" , который пытается аналогичным образом решить ваш вероятный родственный вопрос; вы действительно попробовали мое предложение?

Подробнее о архитектуре adorner см. Обзор Adorners.

Ответ 2

Steffen Opel решил мою проблему со своей ссылкой: Ошибки проверки WPF исчезают внутри TabControl при переключении TabItems.

<TabControl>

  <TabItem>
    <AdornerDecorator>
      <StackPanel>
        ...
      </StackPanel>
    </AdornerDecorator>
  </TabItem>

  <TabItem>
    <AdornerDecorator>
      <StackPanel>
        ...
      </StackPanel>
    </AdornerDecorator>
  </TabItem>

  <TabItem>
    <AdornerDecorator>
      <StackPanel>
        ...
      </StackPanel>
    </AdornerDecorator>
  </TabItem>

</TabControl>

Ответ 3

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

Например:

<ResourceDictionary ...>
    <!-- Add to the default style instead of replacing it -->
    <Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
        <Style.Triggers>
            <MultiTrigger>
                <MultiTrigger.Conditions>
                    <Condition Property="Validation.HasError" Value="True" />
                    <Condition Property="IsVisible" Value="True" />
                </MultiTrigger.Conditions>
                <Setter Property="Validation.ErrorTemplate" Value="{DynamicResource ValidationErrorTemplate}"/>
            </MultiTrigger>
        </Style.Triggers>
    </Style>

    <Style TargetType="PasswordBox" BasedOn="{StaticResource {x:Type PasswordBox}}">
        <Style.Triggers>
            <MultiTrigger>
                <MultiTrigger.Conditions>
                    <Condition Property="Validation.HasError" Value="True" />
                    <Condition Property="IsVisible" Value="True" />
                </MultiTrigger.Conditions>
                <Setter Property="Validation.ErrorTemplate" Value="{DynamicResource ValidationErrorTemplate}"/>
            </MultiTrigger>
        </Style.Triggers>
    </Style>
</ResourceDictionary>

Или, сделайте еще один шаг и избегайте повторения триггера объединяющего стили:

<ResourceDictionary ...>
    <Style x:Key="ErrorControlStyle" TargetType="Control">
        <Style.Triggers>
            <MultiTrigger>
                <MultiTrigger.Conditions>
                    <Condition Property="Validation.HasError" Value="True" />
                    <Condition Property="IsVisible" Value="True" />
                </MultiTrigger.Conditions>
                <Setter Property="Validation.ErrorTemplate" Value="{DynamicResource ValidationErrorTemplate}"/>
            </MultiTrigger>
        </Style.Triggers>
    </Style>

    <Style TargetType="PasswordBox" BasedOn="{extensions:MultiStyle . ErrorControlStyle}"/>
    <Style TargetType="TextBox" BasedOn="{extensions:MultiStyle . ErrorControlStyle}"/>
</ResourceDictionary>

но я бы держался подальше от этого подхода, так как он сломал конструктора.

В приведенных выше примерах я использовал шаблон с именем ValidationErrorTemplate из MahApps.Metro

введите описание изображения здесь

Кроме того, не забудьте также использовать AdornerDecorator внутри исправления TabItem, как описано @Abyte0, чтобы поддерживать проверки при переключении вкладок.