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).
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.
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:
<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?
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. 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:
<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.