【问题标题】:TextBox with validation loses ErrorTemplate on tab change带有验证的文本框在选项卡更改时丢失 ErrorTemplate
【发布时间】:2012-04-12 00:28:10
【问题描述】:

我有一个带有验证规则的 TextBox,它位于 TabControl 的选项卡上。当验证规则失败时,默认的 ErrorTemplate 正确显示(文本框周围的红色边框)。
但是,如果切换到另一个选项卡,然后返回到带有 TextBox 的选项卡,则 ErrorTemplate 突出显示消失了。如果 TextBox 发生更改,则仍会调用验证规则并返回 false,但仍不会显示错误突出显示。
只有当文本内容更改为有效,然后再次变为无效时,highligh 才会恢复。
如果文本内容无效,我希望切换到另一个选项卡并返回会保留无效的突出显示。任何获得这种行为的想法都非常受欢迎。
xml:

<TextBox Height="35" >
  <TextBox.Text>
    <Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <ps:PanIdValidation />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

【问题讨论】:

    标签: wpf validation errortemplate


    【解决方案1】:

    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 提示是在装饰层中绘制的。当您切换选项卡时,该层将被丢弃。

    【讨论】:

    • 太棒了!它也适用于包装多个共享相同样式属性的控件。
    • 很好地使用 AdornerDecorator !
    【解决方案2】:

    只是针对特殊情况的补充:我遇到了类似的问题,现在我正在使用类似于 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. - 但也许它也会在某个时候停止,这是从文档中不清楚。

    【讨论】:

    • 谁会想到这个错误?在我包装了包含几个带有验证的 TextBoxes 的 StackPanel 之后,即使我更改了选项卡项,错误仍然存​​在。谢谢!
    【解决方案3】:

    正如 Dylan 所解释的,这是因为在其中绘制了验证错误的 Adorner 层在选项卡切换时被丢弃了。所以你需要用AdornerDecorator包装内容。

    我创建了一个行为,它将TabItem 的Content 自动包装在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;
                }
            }
        }
    }
    

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

    <Style TargetType="TabItem">
        <Setter Property="b:AdornerBehavior.WrapWithAdornerDecorator" Value="True"></Setter>
    </Style>
    

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

    xmlns:b="clr-namespace:Styling.Behaviors;assembly=Styling"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多