【问题标题】:WPF Error Styles only being rendered properly on visible tab of a tab controlWPF 错误样式仅在选项卡控件的可见选项卡上正确呈现
【发布时间】:2009-09-02 19:26:41
【问题描述】:

我有一个数据对象用于包含支持INotifyPropertyChangedIDataErrorInfo 的UI 数据。最初,我将所有 UI 控件显示在一个大型 WPF 应用程序中,并且很高兴看到通过这种自定义样式标记的错误:

    <!-- Set error style for textboxes -->
    <Style x:Key="txtBoxErrStyle" TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="ToolTip" 
                            Value="{Binding RelativeSource={x:Static RelativeSource.Self}, 
                            Path=(Validation.Errors)[0].ErrorContent}" />
            </Trigger>
        </Style.Triggers>

        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <DockPanel DockPanel.Dock="Right">
                        <AdornedElementPlaceholder />
                        <Image Source="Error.png"
                                   Height="16"
                                   Width="16"
                                   ToolTip="{Binding Path=AdornedElement.ToolTip, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}" />
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

我今天正在重新组织程序,并决定将各种 UI 控件分布在 TabControl 的几页上。我为此使用的结构布局是:

<tabcontrol>
    <tabitem>
        <AdornerDecorator>
           [.. various Stack Panels, Groups and UI controls moved from original layout ..]
        </AdornerDecorator>
    </tabItem>
    <tabitem>
        <AdornerDecorator>
           [.. various Stack Panels, Groups and UI controls moved from original layout ..]
        </AdornerDecorator>
    </tabItem>

    ...
 </tabcontrol>

(我正在使用AdornerDecorator,正如我在之前的程序中所经历的那样,交换标签页时错误样式没有被重新呈现。我不记得我在哪里看到的,但它确实帮助了我。)

现在,当我启动我的程序时,错误样式在程序启动时打开的TabItem 上正确呈现,但在另一个(隐藏)TabItems 上没有正确呈现。当我选择(并显示)其中一个 TabItems 时,设置了错误样式的工具提示,但不显示错误图标图像。

我还测试了删除自定义样式并恢复为文本框的默认 WPF 错误样式,我仍然得到类似的行为,即在程序打开时隐藏的 TabItems 上的控件周围没有红色框。

所以我似乎完全错过了阻止错误样式正确呈现的东西,而不是打开的选项卡项。有什么想法吗?

编辑 9 月 3 日 更改了描述以支持更好地理解我所看到的内容

谈 2014 年的似曾相识

现在是 2014 年 11 月,今天我遇到了这个愚蠢的 WPF 问题,错误模板未显示在选项卡控制器中显示的项目上。我脑海中的某件事表明我以前见过这个问题。于是我用谷歌搜索,首先弹出的是我自己 2009 年的问题!

这次我看到了我上次解决问题后添加的来自 dkl 的评论。所以我尝试了他的方式并使用了这个解决方案(效果很好,我不需要在我的选项卡控件上撒上 Adorner 控件):

<Style x:Key="TextBoxErrorStyle" TargetType="TextBox">
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="Validation.HasError" Value="True" />
                <Condition Property="IsVisible" Value="True" />
            </MultiTrigger.Conditions>
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="True">
                            <TextBlock  DockPanel.Dock="Right" 
                                Foreground="Red"
                                FontSize="14pt" 
                                 Margin="-15,0,0,0" FontWeight="Bold">*
                            </TextBlock>
                            <Border BorderBrush="Red" BorderThickness="2">
                                <AdornedElementPlaceholder Name="controlWithError"/>
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ToolTip" 
                    Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}" />
        </MultiTrigger>
    </Style.Triggers>
</Style>

【问题讨论】:

    标签: c# wpf tabs tabcontrol styles


    【解决方案1】:

    (我正在使用 AdornerDecorator 在以前的计划中经历过 错误样式没有被重新渲染 交换标签页时。我不能 记得我在哪里看到的,但确实如此 帮帮我)

    大概这个非常重要的提示来自Karl Shifflets blog,至少他在讨论同一个主题:WPF Validation Errors Disappear Inside TabControl When Switching TabItems

    鉴于此,您的问题可能只是相关的,即上面的提示/代码确保现在每个选项卡项都有一个专用的AdornerLayer,因为当您切换选项卡时,父元素的装饰层会被丢弃。不过,这个专用的装饰层似乎仍然需要一些特殊处理,例如问题WPF ErrorTemplate visible when not focused?,它基本上是在颠倒处理您的问题。因此,我建议您将后者的概述解决方案与您的风格相结合并扩展,并尝试以下方法(尽管目前尚未测试代码):

    <Style x:Key="ErrorTemplate" TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">...</Trigger>
            <Trigger Property="IsVisible" Value="false">
                <Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
            </Trigger>
            <Trigger Property="IsVisible" Value="true">
                <Setter Property="Validation.ErrorTemplate">
                    <Setter.Value>...</Setter.Value>
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
    

    也请参阅我对您更新 Radio Button Error Style 的评论,它试图类似地解决您可能相关的问题;你真的在那里尝试过我的建议吗?

    有关装饰器架构的更多详细信息,请参阅Adorners Overview

    【讨论】:

    • 再次感谢史蒂芬。我确实看到了你在我的另一个问题上留下的评论,但我想当我遇到这个问题时,我已经忘记了它。
    • 这对我在切换 TabItems 后重新启用验证很有用。 Karl Shifflets 博客上的解决方案不起作用,可能是因为我没有固定数量的 TabItem,而是将 TabItem 动态添加到 TabControl。在这种情况下,似乎需要触发 IsVisible==true 来再次设置 ErrorTemplate。感谢您的提示!
    • 您可以使用 MultiTrigger 使其更简单:&lt;Style x:Key="ErrorTemplate" TargetType="{x:Type TextBox}"&gt; &lt;Style.Triggers&gt; &lt;MultiTrigger&gt; &lt;MultiTrigger.Conditions&gt; &lt;Condition Property="Validation.HasError" Value="True" /&gt; &lt;Condition Property="IsVisible" Value="True" /&gt; &lt;/MultiTrigger.Conditions&gt; &lt;Setter Property="Validation.ErrorTemplate"&gt; &lt;Setter.Value&gt;...&lt;/Setter.Value&gt; &lt;/Setter&gt; &lt;/MultiTrigger&gt; &lt;/Style.Triggers&gt; &lt;/Style&gt;
    • @dkl 5 年后我遇到了同样的问题。他的时间我用了你的想法。谢谢!
    • 优秀的解决方案。我喜欢这比需要在我的所有标签项中定义 AdornerDecorater 好得多。
    【解决方案2】:

    Steffen Opel 通过他的链接解决了我的问题:WPF Validation Errors Disappear Inside TabControl When Switching TabItems.

    <TabControl>
    
      <TabItem>
        <AdornerDecorator>
          <StackPanel>
            ...
          </StackPanel>
        </AdornerDecorator>
      </TabItem>
    
      <TabItem>
        <AdornerDecorator>
          <StackPanel>
            ...
          </StackPanel>
        </AdornerDecorator>
      </TabItem>
    
      <TabItem>
        <AdornerDecorator>
          <StackPanel>
            ...
          </StackPanel>
        </AdornerDecorator>
      </TabItem>
    
    </TabControl>
    

    【讨论】:

    • 既然解不是太大,你能把它粘贴进去吗?您可以使用答案下方的“编辑”链接按钮来更新它。如果链接最终断开,这也将防止您的答案变得无用。
    • 这只是解决方案的一部分。它用于在选项卡之间切换时保持验证不变。它不适用于最初未选中的选项卡。如果在选项卡项内的每个Loaded 事件上设置数据上下文,它可能会起作用,顺便说一下,该事件将在每次选项卡切换时触发。
    【解决方案3】:

    只是为了添加到提供的答案,可以在资源字典中设置一次错误模板。只需为所有相关元素类型的所有默认样式复制和粘贴触发器。

    例如:

    <ResourceDictionary ...>
        <!-- Add to the default style instead of replacing it -->
        <Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="Validation.HasError" Value="True" />
                        <Condition Property="IsVisible" Value="True" />
                    </MultiTrigger.Conditions>
                    <Setter Property="Validation.ErrorTemplate" Value="{DynamicResource ValidationErrorTemplate}"/>
                </MultiTrigger>
            </Style.Triggers>
        </Style>
    
        <Style TargetType="PasswordBox" BasedOn="{StaticResource {x:Type PasswordBox}}">
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="Validation.HasError" Value="True" />
                        <Condition Property="IsVisible" Value="True" />
                    </MultiTrigger.Conditions>
                    <Setter Property="Validation.ErrorTemplate" Value="{DynamicResource ValidationErrorTemplate}"/>
                </MultiTrigger>
            </Style.Triggers>
        </Style>
    </ResourceDictionary>
    

    或者,更进一步,避免combining the styles触发重复:

    <ResourceDictionary ...>
        <Style x:Key="ErrorControlStyle" TargetType="Control">
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="Validation.HasError" Value="True" />
                        <Condition Property="IsVisible" Value="True" />
                    </MultiTrigger.Conditions>
                    <Setter Property="Validation.ErrorTemplate" Value="{DynamicResource ValidationErrorTemplate}"/>
                </MultiTrigger>
            </Style.Triggers>
        </Style>
    
        <Style TargetType="PasswordBox" BasedOn="{extensions:MultiStyle . ErrorControlStyle}"/>
        <Style TargetType="TextBox" BasedOn="{extensions:MultiStyle . ErrorControlStyle}"/>
    </ResourceDictionary>
    

    但我会远离这种方法,因为它会破坏设计师。

    在上面的示例中,我使用了来自 MahApps.Metro 的名为 ValidationErrorTemplate 的模板

    另外,不要忘记在@Abyte0 中描述的TabItem 修复中也使用AdornerDecorator,以便在切换选项卡时保持验证。

    【讨论】:

      猜你喜欢
      • 2011-01-31
      • 1970-01-01
      • 2014-03-05
      • 2011-10-24
      • 2011-06-05
      • 2020-03-04
      • 2012-07-29
      • 2011-01-18
      • 1970-01-01
      相关资源
      最近更新 更多