WPF错误样式只在选项卡控件的可见选项卡上正确显示

17

我有一个数据对象用于包含支持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上,但不会正确地呈现在其他(隐藏的)TabItem上。当我选择(并显示)其中的一个TabItem时,错误样式的工具提示被设置,但错误图标图像没有显示。

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

因此,似乎我完全没有注意到什么东西阻止了错误样式在打开的标签项以外正确呈现。有任何想法吗?

编辑 Sep 3 修改说明以更好地理解我所看到的内容

谈论2014年的Déjà vu

现在是2014年11月,今天我遇到了这个愚蠢的WPF问题,错误模板不显示在标签控制器中呈现的项目上。脑海中的某些东西暗示我以前见过这个问题。所以我Google,第一件事就是我自己在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>
3个回答

21

我使用AdornerDecorator,因为在以前的程序中我遇到了一个错误样式在切换选项卡时未重新渲染的问题。我记不清我在哪里看到这个,但它确实帮助了我。

据推测,这个重要的提示来源于Karl Shifflet的博客,至少他正在处理同样的话题:切换TabControl选项卡时WPF验证错误消失

考虑到这一点,您的问题可能只是相关的。即上面的提示/代码现在确保每个选项卡都有一个专用的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>

请看我对你在单选按钮错误样式更新的评论,它尝试类似地解决你可能相关的问题;你实际上试过我的建议吗?

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


再次感谢你,Steffen。我确实看到了你在我的另一个问题上留下的评论,但我认为当我遇到这个问题时,我已经忘记了它。 - Peter M
1
在我切换TabItems后重新启用验证,这对我很有效。Karl Shifflets博客上的解决方案不起作用,可能是因为我没有固定数量的TabItems,而是动态添加TabItems到TabControl中。在这种情况下,似乎需要触发IsVisible==true以再次设置ErrorTemplate。感谢这个提示! - Slauma
3
你可以使用MultiTrigger使代码变得简单: <Style x:Key="ErrorTemplate" TargetType="{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"> <Setter.Value>...</Setter.Value> </Setter> </MultiTrigger> </Style.Triggers> </Style> - dkl
@dkl 五年后我还是遇到了同样的问题。这一次我使用了你的想法。谢谢! - Peter M
1
非常好的解决方案。我喜欢这种方式,比在所有选项卡中定义AdornerDecorater要好得多。 - Michael Norgren
有时候,当我回到选项卡时,装饰器会显示在屏幕的左上角,而不是控件上。有什么想法吗? - NielW

5

Steffen Opel通过这个链接解决了我的问题: 在切换TabItems时,WPF验证错误在TabControl内消失。

<TabControl>

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

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

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

</TabControl>

由于解决方案不太大,您可以将其粘贴在这里吗?您可以使用答案下面的“编辑”链接按钮来更新它。这也可以防止您的答案在链接最终失效时变得无用。 - ForceMagic
这只是解决方案的一部分。它用于在选项卡之间切换时保持验证完整性。它不会在原本未被选中的选项卡上工作。如果数据上下文在选项卡项内的每个“Loaded”事件上设置,它可能会起作用,顺便说一下,这个事件将在每次选项卡切换时触发。 - MoonStom

1

补充一下之前的答案,错误模板可以在资源字典中设置一次。只需要将触发器复制并粘贴到所有相关元素类型的默认样式中即可。

例如:

<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>

或者,更进一步,通过组合样式来避免触发重复:

<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>

但我建议避免使用这种方法,因为它会破坏设计。
在上面的例子中,我使用了名为ValidationErrorTemplate的模板,来自于MahApps.Metro

enter image description here

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接