带有验证的TextBox在选项卡更改时丢失ErrorTemplate

Ric*_*bob 32 validation wpf errortemplate

我有一个带有验证规则的TextBox,它位于TabControl的选项卡上.验证规则失败时,默认的ErrorTemplate正确显示(TextBox周围的红色边框).
但是,如果切换到另一个选项卡,然后使用TextBox返回选项卡,则ErrorTemplate hightlight将消失.如果TextBox中有更改,则仍会调用验证规则并返回false,但仍未显示错误突出显示.
只有当文本内容被更改为有效然后再次无效时才会重新亮相.
我希望如果文本内容无效,切换到另一个选项卡并返回保持无效突出显示.任何想要获得这种行为的想法都是最受欢
xaml:

<TextBox Height="35" >
  <TextBox.Text>
    <Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <ps:PanIdValidation />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>
Run Code Online (Sandbox Code Playgroud)

Dyl*_*lan 59

TabItem应定义如下:

<TabItem Header="Foo">
    <Border>
        <AdornerDecorator>
            <Grid>
                <TextBox Height="35" >
                    <TextBox.Text>
                         <Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged">
                             <Binding.ValidationRules>
                                 <ps:PanIdValidation />
                             </Binding.ValidationRules>
                          </Binding>
                      </TextBox.Text>
                  </TextBox>
              </Grid>
          </AdornerDecorator>
      </Border>
  </TabItem>
Run Code Online (Sandbox Code Playgroud)

问题是,Validation.Error提示是在Adorner Layer中绘制的.切换选项卡时,该图层将被丢弃.


Vit*_*cik 13

只是特殊情况的补充:我遇到了类似的问题,现在我正在使用类似于Dylan代码的解决方案.

区别在于我的TabItem包含GroupBox,TextBox在其中.在这种情况下,AdornerDecorator必须位于GroupBox本身,而不是TabItem的直接后代.

所以这不起作用:

<TabItem>
    <AdornerDecorator>
        <Grid>
            <GroupBox>
                <Grid>
                    <TextBox>...<TextBox/>
                </Grid>
            </GroupBox>
        </Grid>
    </AdornerDecorator>
</TabItem>
Run Code Online (Sandbox Code Playgroud)

但这样做:

<TabItem>
    <Grid>
        <GroupBox>
            <AdornerDecorator>
                <Grid>
                    <TextBox>...<TextBox/>
                </Grid>
            </AdornerDecorator>
        </GroupBox>
    </Grid>
</TabItem>
Run Code Online (Sandbox Code Playgroud)

我正在添加它,因为我无法轻易找到解决方案,甚至文档AdornerLayer.GetAdornerLayer()(虽然不确定它是否适用于此处)状态This static method traverses up the visual tree starting at the specified Visual and returns the first adorner layer found.- 但也许它也会在某些时候停止,这在文档中并不清楚.


Dom*_*see 6

正如Dylan解释的那样,这是因为在制表符开关上丢弃了用于绘制验证错误的Adorner层.所以你需要包装内容AdornerDecorator.

我创建了一个行为,一个包装ContentTabItem自动的AdornerDecorator,所以它不必须手动完成所有的TabItems.

public static class AdornerBehavior
{
    public static bool GetWrapWithAdornerDecorator(TabItem tabItem)
    {
        return (bool)tabItem.GetValue(WrapWithAdornerDecoratorProperty);
    }
    public static void SetWrapWithAdornerDecorator(TabItem tabItem, bool value)
    {
        tabItem.SetValue(WrapWithAdornerDecoratorProperty, value);
    }

    // Using a DependencyProperty as the backing store for WrapWithAdornerDecorator.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty WrapWithAdornerDecoratorProperty =
        DependencyProperty.RegisterAttached("WrapWithAdornerDecorator", typeof(bool), typeof(AdornerBehavior), new UIPropertyMetadata(false, OnWrapWithAdornerDecoratorChanged));

    public static void OnWrapWithAdornerDecoratorChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var tabItem = o as TabItem;
        if (tabItem == null) return;

        if(e.NewValue as bool? == true)
        {
            if (tabItem.Content is AdornerDecorator) return;
            var content = tabItem.Content as UIElement;
            tabItem.Content = null;
            tabItem.Content = new AdornerDecorator { Child = content };
        }
        if(e.NewValue as bool? == false)
        {
            if (tabItem.Content is AdornerDecorator)
            {
                var decorator= tabItem.Content as AdornerDecorator;
                var content = decorator.Child;
                decorator.Child = null;
                tabItem.Content = content;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以TabItems通过默认样式在所有行为上设置此行为:

<Style TargetType="TabItem">
    <Setter Property="b:AdornerBehavior.WrapWithAdornerDecorator" Value="True"></Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)

b 是行为所在的命名空间,类似这样(每个项目都会有所不同):

xmlns:b="clr-namespace:Styling.Behaviors;assembly=Styling"
Run Code Online (Sandbox Code Playgroud)