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

时间:2012-03-28 12:02:31

标签: wpf validation 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>

3 个答案:

答案 0 :(得分:58)

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>

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

答案 1 :(得分:11)

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

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

所以这不起作用:

<TabItem>
    <AdornerDecorator>
        <Grid>
            <GroupBox>
                <Grid>
                    <TextBox>...<TextBox/>
                </Grid>
            </GroupBox>
        </Grid>
    </AdornerDecorator>
</TabItem>

但是这样做了:

<TabItem>
    <Grid>
        <GroupBox>
            <AdornerDecorator>
                <Grid>
                    <TextBox>...<TextBox/>
                </Grid>
            </AdornerDecorator>
        </GroupBox>
    </Grid>
</TabItem>

我正在添加它,因为我无法轻松找到解决方案,甚至AdornerLayer.GetAdornerLayer()的文档(虽然不确定它是否适用于此处)表明This static method traverses up the visual tree starting at the specified Visual and returns the first adorner layer found. - 但也许它也停留在某些地方从文档中可以看出这一点。

答案 2 :(得分:6)

正如Dylan解释的那样,这是因为在Tab键切换时会丢弃其中绘制了验证错误的Adorner层。因此,您需要使用return "nothing".equalsIgnoreCase(message) ? "Are you sure?" : "yes".equalsIgnoreCase(message) ? "ok" : null; /* if message == "nothing" return "Are you sure?" if message == "yes" return "ok" if message != "yes" AND message != "nothing" return null P.S: '==' and '!=' are for demonstration only. Keep using the .equals() method for matching strings */ 包装内容。

我创建了一个行为,会自动将AdornerDecorator Content包裹在TabItem中,这样就不必完成在所有TabItems上手动操作。

AdornerDecorator

您可以通过默认样式在所有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; } } } } 上设置此行为:

TabItems

<Style TargetType="TabItem"> <Setter Property="b:AdornerBehavior.WrapWithAdornerDecorator" Value="True"></Setter> </Style> 是行为所在的命名空间,类似这样(每个项目都会有所不同):

b