带验证的 WPF TextBox 丢失 ErrorTemplate

WPF TextBox with validation loses ErrorTemplate

我遇到的问题与这些非常相似:

Issue with WPF validation(IDataErrorInfo) and tab focusing

TextBox with validation loses ErrorTemplate on tab change

AdornerDecoratorWindow 的同一实例 中执行技巧 ,但是当重新加载 Window 并且我切换到TabItem 包含错误的 TextBoxErrorTemplate 将不再显示

<Window x:Class="Views.MyWindowView">
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TabControl HorizontalAlignment="Stretch" 
                    Height="Auto"
                    VerticalAlignment="Top"
                    Width="Auto"
                    SelectionChanged="TabItemChanged"
                    Name="MyTabControl">

            <!-- Below, AdornerDecorator are added for the following reason:
                 the Validation.Error cues are painted in the Adorner Layer. 
                 When tabs are switched, that layer is discarded. -->

            <!-- The view 1 tab.-->
            <TabItem Header="{Resx tab1_Header}"
                     Name="Tbi1">
                <AdornerDecorator>
                    <vw:MyView1 DataContext="{Binding}"/>
                </AdornerDecorator>
            </TabItem>

            <!-- The view 2 tab.-->
            <TabItem Header="{Resx tab2_Header}"
                     Name="Tbi2">
                <AdornerDecorator>
                    <vw:MyView2 DataContext="{Binding}"/>
                </AdornerDecorator>
            </TabItem>
        </TabControl>

...

我试图在 TabControl SelectionChanged 的代码隐藏中重新触发验证,但没有成功。

有什么想法吗?

我在 WPF 中看到了一些奇怪的事情,并会提出这个建议。根据您引用并尝试在此处应用装饰器装饰器包装视图的其他帖子,将装饰器的包装直接移动到您的 "MvView1" XAML 页面中。

从您现有的...

<AdornerDecorator>
   <vw:MyView1 DataContext="{Binding}"/>
</AdornerDecorator>

改为...

<vw:MyView1 DataContext="{Binding}"/>

并在

<XAML for MyView1>
   <AdornerDecorator>
      [wrapping is within the view... then the rest of the view...]
   </AdornerDecorator>
</close XAML for MyView1>

我注意到类似的情况,例如应用控件的可见性,除非某些东西在一个级别上有效,而在另一个级别上则无效。虽然从未找到确切的模式。

拼图

AdornerLayer 表示用于渲染装饰器的表面。 由于 AdornerLayer 通常服务于整个视图,而不仅仅是一个控件,一些容器默认实现它们。

adorner 是绑定到 UIElement 的自定义 FrameworkElement。装饰器在 AdornerLayer 中呈现,这是一个始终位于装饰元素或装饰元素集合之上的渲染表面。

所以在这种情况下,装饰器(红色矩形)绑定到 TextBox,但在 TextBox.

顶部的图层中呈现

装饰(例如,在验证错误的情况下)是通过调用静态方法 GetAdornerLayer 来获取要装饰的 UIElementAdornerLayer 对象。

足够的理论

更改 TabItems 会丢弃 AdornerLayer,导致装饰器未被绘制。 2 个修复:

\DRapp建议的手动方式:

<XAML for MyView1>
   <AdornerDecorator>
      ...
   </AdornerDecorator>
</close XAML for MyView1>

当然,如果在 AdornerDecoratorTextBox 之间(在可视化树中)有另一个容器实现了 AdornerLayer,这不会有任何好处。所以显式 AdornerDecorator 需要是最后一个包装 TextBox.

<XAML for MyView1>
    <Grid>

        ...

        <GroupBox>
            <AdornerDecorator>
                <Grid>

                    ...

                    <TextBox ... />
                </Grid>
            </AdornerDecorator>
        </GroupBox>
    </Grid>
</close XAML for MyView1>

\第二种解决方案(我更喜欢)在每次 TextBox 可见时重置 ErrorTemplate。在这样做的过程中,缺少 AdornerLayer 的问题会被发现并修复。

<UserControl.Resources>
    <Style TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="IsVisible" Value="true">
                <Setter Property="Validation.ErrorTemplate">
                    <Setter.Value>
                        <ControlTemplate>
                            <Border BorderBrush="Red" BorderThickness="1">
                                <AdornedElementPlaceholder/>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>