在 WPF 4.5 中使用 INotifyDataErrorInfo 创建一个显示控件的所有 Validation.Errors 的工具提示
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22912637/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating a ToolTip that shows all Validation.Errors for a control using INotifyDataErrorInfo in WPF 4.5
提问by Matt Zappitello
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. 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. I return an Error object in my INotifyDataErrorInfo implementation that specifies the severity of an error (Error or Warning).
我有多个控件,包括一个 TextBox 和一个 ComboBox,我希望所有控件都显示一个工具提示,其中包含 Validation.Errors 集合中包含的所有错误。如果可能的话,我希望他们都能分享一个共同的风格,这就是我正在尝试的。我确信我在 ToolTip setter 中的绑定有问题,但我不知道是什么。我在 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. 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 上搜索 AncesstoryType 控件。这些似乎都不起作用。
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. 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. The ToolTip for the style has no such luck. Here is the working ControlTemplate I used for my ErrorTemplate:
我基于我用于 ErrorTemplate 的 ControlTemplate 的样式,它的作用几乎相同:它根据错误严重程度显示红色或黄色边框,并显示与我想为控件上的 ToolTip 所做的完全一样的 ToolTip本身。我确定它与我的绑定有关,因为 ErrorTemplate 自动将其 DataContext 设置为 Validation.Errors 集合,这使得绑定 ItmesCollection 的 ItemsSource 变得容易。样式的工具提示没有这样的运气。这是我用于 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?
任何人都可以给我任何建议吗?
采纳答案by Matt Zappitello
After having tried to figure this out for quite some time, I finally stumbled on a post on the MSDN forumsthat led me down the right path. First of all, I needed to specify Styles for each TargetType that I wanted and base them on the original. 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. Here is my new Style:
在试图弄清楚这一点一段时间后,我终于在 MSDN 论坛上偶然发现了一篇文章,它引导我走上了正确的道路。首先,我需要为我想要的每个 TargetType 指定样式,并基于原始样式。其次,我意识到问题不在于我的绑定,而在于绑定的集合没有被更新。我不知道为什么我的 ListBox/ItemsControl 在 XAML 中指定时没有更新,但如果您在转换器中指定它,它确实有效。这是我的新风格:
<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.
我希望这对有同样问题的其他人有所帮助。如果有人知道为什么这会产生如此大的不同,我很想知道。
回答by Adam
This could be achieved much easier.
这可以更容易地实现。
If you write the binding to "Tooltip" as above:
如果您将绑定写入“工具提示”,如上所示:
<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. Thus to the control it is attached.
Binding“奇迹般地”实际上将自身重新绑定到工具提示的“PlacementTarget”。因此它被附加到控件上。
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. Then just use the Tooltip as the RelativeSource via AncestorType on the ItemsControl.
您甚至可以删除 Tooltip 对象并直接从 ItemsControl 绑定到 PlacementTarget。然后只需通过 ItemsControl 上的 AncestorType 使用 Tooltip 作为 RelativeSource。
Hope this helps :)
希望这可以帮助 :)
回答by Martin
This is just a more simplified version of Matts answer that I used. It works only for controls of type TextBoxand does not use an error severity.
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 答案的更简化版本。它仅适用于类型控件,TextBox不使用错误严重性。当我需要在用户输入的目录路径的上下文中显示一个或多个错误时,我曾使用过这种情况。与 Matts 的回答相反,我使用 DisplayMemberPath = "ErrorContent" 直接在转换器中访问错误。
A style for a TextBoxwhich shows the tooltip, when the attached property Validation.HasErroris true:
TextBox当附加属性Validation.HasError为 true时,显示工具提示的
a 样式:
<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" TextBoximplicitly using the style:
我的“目录”TextBox隐式使用样式:
<TextBox Text="{Binding Directory, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"></TextBox>
The value converter directly accessing the ErrorContentproperty:
直接访问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();
}
}

