WPF 错误样式仅在选项卡控件的可见选项卡上正确呈现

发布于 2024-08-03 20:53:00 字数 4170 浏览 5 评论 0 原文

我有一个数据对象,用于包含支持 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 控件周围没有红色框打开。

所以看来我完全错过了一些阻止错误样式在打开的选项卡项之外正确呈现的东西。有什么想法吗?

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

谈论2014年的似曾相识

现在是2014年11月,今天我遇到了这个愚蠢的WPF问题,错误模板没有显示在选项卡控制器中呈现的项目上。我内心深处的某些想法表明我以前见过这个问题。于是我用谷歌搜索,第一个出现的就是我自己 2009 年提出的问题!

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

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

I have a data object used to contain my UI data that supports INotifyPropertyChanged and IDataErrorInfo. Originally I had all of the UI controls displaying in one big WPF application and was happily seeing errors flagged via this custom style:

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

I was reorganizing the program today and decided to distribute the various UI controls over several pages of a TabControl. The structure layout I am using for this is:

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

(I am using the AdornerDecorator as I had experienced in a previous program the error style not being re-rendered when swapping tab pages. I can't remember where I saw this but it did help me out.)

Now when I start my program the error style correctly renders on the TabItem that is open when the program starts, but does not correctly render on the other (hidden) TabItems. When I select (and reveal) one of those TabItems the tool-tip of the error style is set, but the error icon image is not displayed.

I also tested removing the custom style and revert back to the default WPF error style for textboxes and I still get a similar behaviour, i.e. no red box around the control on the TabItems that are hidden when the program opens.

So it seems that I am totally missing something that is stopping the error styles from correctly rendering on other than the open tab Item. Any ideas?

Edit Sep 3 Changed description to support a better understanding of what I have seen

Talk about Déjà vu in 2014

It's November 2014 and today I had this stupid WPF problem with error templates not showing up on items presented in a tab controller. Something in the back of my mind suggests that I have seen this problem before. So I google, and the first thing that pops up is my own question from 2009!

This time I see the comment from dkl which was added after I solved things the last time around. So I tried it his way and used this solution (which worked well and I didn't need to sprinkle an Adorner control over my tab controls):

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

纵山崖 2024-08-10 20:53:00

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

大概这个确实重要的提示源自 Karl Shifflets 博客,至少他正在讨论相同的主题: WPF 验证错误切换 TabItems 时消失在 TabControl 内

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

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

请参阅我关于您更新 单选按钮错误样式,它试图以类似的方式解决您可能相关的问题;你真的尝试过我的建议吗?

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

(I am using the AdornerDecorator as I
had experienced in a previous program
the error style not being re-rendered
when swapping tab pages. I can't
remember where I saw this but it did
help me out)

Presumably this indeed important tip originates from Karl Shifflets blog, at least he's addressing the same topic: WPF Validation Errors Disappear Inside TabControl When Switching TabItems.

Given this your issue might just be related, i.e. the tip/code above ensures there is a dedicated AdornerLayer for every tab item now, as the adorner layer of the parent element is discarded when you switch tabs. This dedicated adorner layer appears to still require some special treatment though, see for example question WPF ErrorTemplate visible when not focused? which is basically dealing with your issue upside down. Consequently I'd suggest you combine and expand the outlined solution for the latter with your style and try the following (untested code as of now though):

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

See my comment regarding your update of Radio Button Error Style too, which tries to similarly address your likely related question; have you actually tried my suggestion there?

See Adorners Overview for more details on the adorner architecture.

心不设防 2024-08-10 20:53:00

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

<TabControl>

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

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

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

</TabControl>

Steffen Opel solved my problem with his link : 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>
说谎友 2024-08-10 20:53:00

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

例如:

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

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

在上面的示例中,我使用了来自 MahApps.Metro

在此处输入图像描述

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

Just to add to the answer provided, the error template can be set once in a resource dictionary. Just the triggers have to be copied & pasted for all the default styles of all the relevant element types.

For example:

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

Or, take it one step further and avoid the trigger repetition by 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>

but I'd stay away from this approach as it will break the designer.

In the examples above I used the template named ValidationErrorTemplate from MahApps.Metro

enter image description here

Also, don't forget to also use the AdornerDecorator inside the TabItem fix, as described by @Abyte0, in order to maintain the validations when tab switching.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文