简体   繁体   English

创建一个ToolTip,在WPF 4.5中使用INotifyDataErrorInfo显示控件的所有Validation.Errors

[英]Creating a ToolTip that shows all Validation.Errors for a control using INotifyDataErrorInfo in WPF 4.5

I have multiple controls including a TextBox and a ComboBox and I would like all of them to display a ToolTip with all of the errors contained in the Validation.Errors collection. 我有多个控件,包括TextBox和ComboBox,我希望所有控件都显示一个ToolTip,其中包含Validation.Errors集合中包含的所有错误。 I would like them all to share a common style if possible, which is what I am trying. 如果可能的话,我希望他们都能分享一种共同的风格,这就是我的努力。 I am convinced I am doing something wrong with my binding in the ToolTip setter, but I can't figure out what. 我确信我在ToolTip setter中的绑定有问题,但我无法弄清楚是什么。 I return an Error object in my INotifyDataErrorInfo implementation that specifies the severity of an error (Error or Warning). 我在INotifyDataErrorInfo实现中返回一个Error对象,该对象指定错误的严重性(错误或警告)。

I would like to have a style that applies to all controls in the Window that would display a ToolTip containing a list of all errors and warnings for that control. 我希望有一个适用于Window中所有控件的样式,它将显示一个ToolTip,其中包含该控件的所有错误和警告的列表。 The errors should be displayed in red and the warnings in yellow. 错误应显示为红色,警告显示为黄色。 Here is the Style I have come up with: 这是我提出的风格:

        <Style TargetType="FrameworkElement">
        <Setter Property="ToolTip">
            <Setter.Value>
                <ItemsControl ItemsSource="{Binding Path=(Validation.Errors), RelativeSource={RelativeSource Self}}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding ErrorContent.ErrorMessage}">
                                <TextBlock.Style>
                                    <Style TargetType="TextBlock">
                                        <Setter Property="Foreground" Value="Red"/>
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding ErrorContent.ErrorSeverity}"
                                                             Value="{x:Static local:ErrorType.Warning}">
                                                <Setter Property="Foreground" Value="Yellow"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBlock.Style>
                            </TextBlock>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=(Validation.HasError)}" Value="True">
                <Setter Property="ToolTip">
                    <Setter.Value>
                        <ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding ErrorContent.ErrorMessage}">
                                        <TextBlock.Style>
                                            <Style TargetType="TextBlock">
                                                <Setter Property="Foreground" Value="Red"/>
                                                <Style.Triggers>
                                                    <DataTrigger Binding="{Binding ErrorContent.ErrorSeverity}"
                                                             Value="{x:Static local:ErrorType.Warning}">
                                                        <Setter Property="Foreground" Value="Yellow"/>
                                                    </DataTrigger>
                                                </Style.Triggers>
                                            </Style>
                                        </TextBlock.Style>
                                    </TextBlock>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>

I have tried changing the RelativeSource to search for an AncestoryType of Control at both AncestorLevel 1 and 2. None of this seems to work. 我已经尝试更改RelativeSource以在AncestorLevel 1和2处搜索AncestoryType of Control。这似乎都不起作用。

I based the style on a ControlTemplate that I used for the ErrorTemplate that does pretty much the same thing: it displays a red or yellow border depending on the error severity and displays a ToolTip exactly like what I want to do for the ToolTip on the control itself. 我将这个样式基于我用于ErrorTemplate的ControlTemplate,它做了几乎相同的事情:它根据错误严重程度显示红色或黄色边框,并显示一个工具提示,就像我想要对控件上的ToolTip做的那样本身。 I'm certain it has something to do with my binding, because the ErrorTemplate automatically has its DataContext set to the Validation.Errors collection, which makes it easy to bind the ItemsSource for the ItmesCollection. 我确定它与我的绑定有关,因为ErrorTemplate自动将其DataContext设置为Validation.Errors集合,这使得为ItmesCollection绑定ItemsSource变得容易。 The ToolTip for the style has no such luck. 风格的工具提示没有这样的运气。 Here is the working ControlTemplate I used for my ErrorTemplate: 这是我用于ErrorTemplate的工作ControlTemplate:

        <ControlTemplate x:Key="ErrorTemplate">
        <Border BorderThickness="1">
            <AdornedElementPlaceholder Name="ElementPlaceholder"/>
            <Border.Style>
                <Style TargetType="Border">
                    <Setter Property="BorderBrush" Value="Red"/>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=ElementPlaceholder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent.ErrorSeverity}"
                                         Value="{x:Static local:ErrorType.Warning}">
                            <Setter Property="BorderBrush" Value="Yellow"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Border.Style>
            <Border.ToolTip>
                <ItemsControl ItemsSource="{Binding}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding ErrorContent.ErrorMessage}">
                                <TextBlock.Style>
                                    <Style TargetType="TextBlock">
                                        <Setter Property="Foreground" Value="Red"/>
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding ErrorContent.ErrorSeverity}"
                                                             Value="{x:Static local:ErrorType.Warning}">
                                                <Setter Property="Foreground" Value="Yellow"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBlock.Style>
                            </TextBlock>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Border.ToolTip>
        </Border>
    </ControlTemplate>

Can anyone give me any suggestions? 谁能给我任何建议?

This could be achieved much easier. 这可以更容易实现。

If you write the binding to "Tooltip" as above: 如果您将绑定写入“Tooltip”,如上所示:

<Trigger Property="Validation.HasError" Value="True">
      <Setter Property="ToolTip"
              Value="{Binding RelativeSource={RelativeSource Self},  Path=(Validation.Errors), Converter={StaticResource ErrorCollectionConverter}}">
      </Setter>
</Trigger>

The Binding "miraculously" actually rebinds itself to the "PlacementTarget" of the tooltip. Binding“奇迹般地”实际上将自己重新绑定到工具提示的“PlacementTarget”。 Thus to the control it is attached. 因此对于控制它是附着的。

If you need to display the full list of items you can do the below: 如果您需要显示完整的项目列表,您可以执行以下操作:

<Trigger Property="Validation.HasError" Value="True">
      <Setter Property="ToolTip">
          <Setter.Value>
               <ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}">
                   <ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}" DisplayMemberPath="ErrorContent" />
               </ToolTip>
          </Setter.Value>
      </Setter>
</Trigger>

You could even drop the Tooltip object and bind to the PlacementTarget directly from the ItemsControl. 您甚至可以删除Tooltip对象并直接从ItemsControl绑定到PlacementTarget。 Then just use the Tooltip as the RelativeSource via AncestorType on the ItemsControl. 然后在ItemsControl上通过AncestorType使用Tooltip作为RelativeSource。

Hope this helps :) 希望这可以帮助 :)

After having tried to figure this out for quite some time, I finally stumbled on a post on the MSDN forums that led me down the right path. 在试图解决这个问题很长一段时间之后,我终于偶然发现MSDN论坛上的一篇帖子让我走上了正确的道路。 First of all, I needed to specify Styles for each TargetType that I wanted and base them on the original. 首先,我需要为我想要的每个TargetType指定样式并将它们基于原始样式。 Secondly, I realized that the problem was not in my binding, but in the fact that the collection that was bound to was not being updated. 其次,我意识到问题不在于我的绑定,而在于绑定的集合没有被更新。 I have no idea why my ListBox/ItemsControl was not being updated when specified in XAML, but it does work if you specify it in a converter. 我不知道为什么我的ListBox / ItemsControl在XAML中指定时没有更新,但如果你在转换器中指定它,它确实有效。 Here is my new Style: 这是我的新风格:

        <Style TargetType="Control" x:Key="ErrorToolTip">
        <Style.Resources>
            <Style TargetType="ListBoxItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <TextBlock Text="{Binding ErrorContent.ErrorMessage}"
                                       Background="Transparent">
                                <TextBlock.Style>
                                    <Style TargetType="TextBlock">
                                        <Setter Property="Foreground" Value="Red"/>
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding ErrorContent.ErrorSeverity}"
                                                         Value="{x:Static local:ErrorType.Warning}">
                                                <Setter Property="Foreground" Value="Orange" />
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBlock.Style>
                            </TextBlock>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Style.Resources>
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors), Converter={StaticResource ErrorCollectionConverter}}">
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style TargetType="TextBox" BasedOn="{StaticResource ErrorToolTip}"/>
    <Style TargetType="ComboBox" BasedOn="{StaticResource ErrorToolTip}"/>

And here is my converter function: 这是我的转换器功能:

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) return null;
        return new ListBox
        {
            ItemsSource = (ReadOnlyObservableCollection<ValidationError>) value,
            BorderThickness = new Thickness(0),
            Background = Brushes.Transparent
        };
    }

I hope this is helpful to any others out there that are having my same problem. 我希望这对那些有同样问题的人有帮助。 If someone knows why this makes such a big difference, I would love to know. 如果有人知道为什么会有这么大的差异,我很想知道。

This is just a more simplified version of Matts answer that I used. 这只是我使用的Matts答案的更简化版本。 It works only for controls of type TextBox and does not use an error severity. 它仅适用于TextBox类型的TextBox ,不使用错误严重性。 I used for a case when I needed to display one or more errors in context of a directory path entered by the user. 当我需要在用户输入的目录路径的上下文中显示一个或多个错误时,我使用了一个案例。 In contrast to Matts answer I used DisplayMemberPath = "ErrorContent" to access the errors directly in the converter. 与Matts回答相反,我使用DisplayMemberPath =“ErrorContent”直接在转换器中访问错误。


A style for a TextBox which shows the tooltip, when the attached property Validation.HasError is true: 当附加属性Validation.HasError为true时, TextBox的样式显示工具提示:

<UserControl.Resources>
    <ui:ErrorCollectionConverter x:Key="ErrorCollectionConverter"></ui:ErrorCollectionConverter>
    <Style TargetType="TextBox">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="ToolTip"
                    Value="{Binding RelativeSource={RelativeSource Self},  Path=(Validation.Errors), Converter={StaticResource ErrorCollectionConverter}}">
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>

My "Directory" TextBox implicitly using the style: 我的“目录” TextBox隐式使用样式:

<TextBox Text="{Binding Directory, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"></TextBox>

The value converter directly accessing the ErrorContent property: 值转换器直接访问ErrorContent属性:

internal class ErrorCollectionConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) return null;
        return new ListBox
        {
            ItemsSource = (ReadOnlyObservableCollection<ValidationError>)value,
            BorderThickness = new Thickness(0),
            Background = Brushes.Transparent,
            DisplayMemberPath = "ErrorContent"
        };
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}   

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM