Я не уверен, что это лучшее решение, но оно работает для меня. Обычно я обращаюсь с этим, создавая DataTrigger, связанный с логическим свойством, которое указывает на допустимость или недопустимость.
Если логическое значение равно false, я выделяю границу красным цветом. Если нет, то цвет рамки отсутствует.
Вот пример DataTrigger:
<Style TargetType="{x:Type TextBox}">
<Setter Property="TextElement.FontFamily" Value="Calibri" />
<Setter Property="TextElement.FontSize" Value="14" />
<Setter Property="TextElement.Foreground" Value="Black" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsValid}" Value="False">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="5" Color="Red" ShadowDepth="0" />
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Message Field entered does not exist in Message Output tree." />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsValid}" Value="True">
<Setter Property="Effect" Value="{x:Null}" />
</DataTrigger>
</Style.Triggers>
</Style>
Если вы примените этот стиль к обоим нашим текстовым полям, привязанным к одному и тому же логическому значению, они оба будут отображаться с ярко-красной рамкой, когда логическое значение равно false.