Подтвердить что ты не робот

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

Я пытаюсь выполнить проверку в моем приложении WPF с помощью интерфейса IDataErrorInfo, и я столкнулся с не очень желательной ситуацией.

У меня есть этот шаблон, который используется, когда элемент управления не проверяет

<ControlTemplate x:Key="errorTemplate">
    <DockPanel LastChildFill="true">
        <Border Background="Red" DockPanel.Dock="Right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10"
                                    ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
            <TextBlock Text="!" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" Foreground="White" />
        </Border>
        <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
            <Border BorderBrush="red" BorderThickness="1" />
        </AdornedElementPlaceholder>
    </DockPanel>
</ControlTemplate>

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

Normal displayDisplay when part of the control is hidden

Как я могу избежать этого и сделать мой шаблон ошибки отображаемым под элементом док-станции, как и должно быть?

ИЗМЕНИТЬ

Я обнаружил, что могу поместить мой TextBox с помощью AdornerDecorator, чтобы исправить это, но я действительно не хочу делать это для каждого элемента управления TextBox в моем приложении. Может ли быть способ установить его с помощью Style или каким-либо другим способом?

РЕДАКТИРОВАТЬ 2

Возможно, я мог бы изменить стандартный TextBox ControlTemplate по умолчанию, чтобы включить AdornerDecorator, но я не слишком сильно хочу изменить какие-либо шаблоны управления по умолчанию WPF. Любые другие предложения приветствуются.

4b9b3361

Ответ 1

ОК, я нашел относительно простое решение, которое не заставляет меня изменять какие-либо шаблоны управления.

Вместо украшения каждого TextBox с помощью AdornerDecorator, подобного этому

<StackPanel>
    <AdornerDecorator>
        <TextBox Text={Binding ...} />
    </AdornerDecorator>
    <AdornerDecorator>
        <TextBox Text={Binding ...} />
    </AdornerDecorator>
</StackPanel>

Я могу обернуть AdornerDecorator весь мой вид, который достигает того же результата.

<AdornerDecorator>
    <StackPanel>
        <TextBox Text={Binding ...} />
        <TextBox Text={Binding ...} />
    </StackPanel>
</AdornerDecorator>

Таким образом, я могу определить его не более одного раза для каждого представления.

Ответ 2

На основе замечательного ответа @AdiLester, если ваши элементы управления выведены из базового класса, и вы не хотите помещать AdornerDecorator в XAML каждого элемента управления, выполните следующие действия:

public class MyBaseUserControl : UserControl
{
    public MyBaseUserControl()
    {

    }

    protected override void OnContentChanged(object oldContent, object newContent)
    {
        base.OnContentChanged(oldContent, newContent);

        if (!(newContent is AdornerDecorator))
        {
            this.RemoveLogicalChild(newContent);

            var decorator = new AdornerDecorator();
            decorator.Child = newContent as UIElement;

            this.Content = decorator;
        }
    }
}

Ответ 3

Я бы использовал стиль, и вот здесь пример того, который можно легко адаптировать.

Обратите внимание, что ErrorContent исходит из (Validation.Errors).CurrentItem.ErrorContent в отличие от Errors [0]. Хотя оба они будут работать, последний будет засорять ваше окно вывода с пропущенными исключениями, как описанный здесь.

<Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
    <Setter Property="Margin" Value="0,0,16,0" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="VerticalContentAlignment" Value="Center" />

    <!--
    Error handling
    -->
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <DockPanel LastChildFill="True">
                    <TextBlock DockPanel.Dock="Right" Text=" *" 
                               Foreground="Red" FontWeight="Bold" FontSize="16" 
                               ToolTip="{Binding ElementName=placeholder, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
                    <Border BorderBrush="Red"  BorderThickness="1">
                        <AdornedElementPlaceholder Name="placeholder"></AdornedElementPlaceholder>
                    </Border>
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="Background" Value="LightYellow"/>
        </Trigger>
    </Style.Triggers>
</Style>