Why does WPF Style to show validation errors in ToolTip work for a TextBox but fails for a ComboBox?
I am using a typical Style to display validation errors as a tooltip from IErrorDataInfo for a textbox as shown below and it works fine.
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
But when i try to do the same thing for a ComboBox like this it fails
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
The error I get in the output window is:
System.Windows.Data Error: 17 : Cannot get 'Item[]' value (type 'ValidationError') from '(Validation.Errors)' (type 'ReadOnlyObservableCollection`1'). BindingExpression:Path=(0)[0].ErrorContent; DataItem='ComboBox' (Name='ownerComboBox'); target element is 'ComboBox' (Name='ownerComboBox'); target property is 'ToolTip' (type 'Object') ArgumentOutOfRangeException:'System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.Parameter name: index'
Oddly it also attempts to make invalid Database changes when I close the window if I change any ComboBox values (This is also when the binding error occurs)!!!
Cannot insert the value NULL into column 'EmpFirstName', table 'OITaskManager.dbo.Employees'; column does not allow nulls. INSERT fails. The statement has been terminated.
Simply by commenting the style out everyting works perfectly. How do I fix this?
Just in case anyone needs it one of the comboBox' xaml follows:
<ComboBox ItemsSource="{Binding Path=Employees}"
SelectedValuePath="EmpID"
SelectedValue="{Binding Path=SelectedIssue.Employee2.EmpID,
Mode=OneWay, ValidatesOnDataErrors=True}"
ItemTemplate="{StaticResource LastNameFirstComboBoxTemplate}"
Height="28" Name="ownerComboBox" Width="120" Margin="2"
SelectionChanged="ownerComboBox_SelectionChanged" />
<DataTemplate x:Key="LastNameFirstComboBoxTemplate">
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{1}, {0}" >
<Binding Path="EmpFirstName" />
<Binding Path="EmpLastName" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
SelectionChanged: (I do plan to implement commanding before long but, as this is my first WPF project I have not gone full MVVM yet. I am trying to take things in small-medium sized bites)
// This is done this way to maintain the DataContext Integrity
// and avoid an error due to an Object being "Not New" in Linq-to-SQL
private void ownerComboBox_SelectionChanged(object se开发者_开发百科nder,
SelectionChangedEventArgs e)
{
Employee currentEmpl = ownerComboBox.SelectedItem as Employee;
if (currentEmpl != null &&
currentEmpl != statusBoardViewModel.SelectedIssue.Employee2)
{
statusBoardViewModel.SelectedIssue.Employee2 = currentEmpl;
}
}
Your getting this error because when you validation finds that there are no issues, the Errors collection returns with no items, and the following binding logic fails:
Path=(Validation.Errors)[0].ErrorContent}"
you are accessing the validation collection by a specific index. I'm currently working on a DataTemplate Suggestion for replacing this text.
I love that Microsoft listed this in their standard example of a validation template.
update so replace the code above with the following, and the binding logic will know how to handle the empty validationresult collection:
Path=(Validation.Errors).CurrentItem.ErrorContent}"
(following xaml was added as a comment)
<ControlTemplate x:Key="ValidationErrorTemplate" TargetType="Control">
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Red" FontSize="24" Text="*"
ToolTip="{Binding .CurrentItem}">
</TextBlock>
<AdornedElementPlaceholder>
</AdornedElementPlaceholder>
</StackPanel>
</ControlTemplate>
Update in 2019
As of currently, the correct path syntax to use is:
Path=(Validation.Errors)/ErrorContent
I think this is the best way:
Path=(Validation.Errors)/ErrorContent
/
is actually equal to CurrentItem
by @Nathan
In my case, CurrentItem
is a no go.
Try the converter for converting to a multi-line string as described here
In my case, I was getting this exception when I tried to apply @Nation Tregillus' solution:
Cannot resolve property 'CurrentItem' in data context of type 'System.Collections.ObjectModel.ReadOnlyObservableCollection'
So I went with @Altiano Gerung's solution instead, where my code ended up being:
<ControlTemplate x:Key="ValidationErrorTemplate">
<DockPanel Margin="5,0,36,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" DockPanel.Dock="Right"
Margin="5,0,36,0"
ToolTip="{Binding ElementName=ErrorAdorner, Path=AdornedElement.(Validation.Errors)/ErrorContent}"
ToolTipService.ShowDuration="300000"
ToolTipService.InitialShowDelay="0"
ToolTipService.BetweenShowDelay="0"
ToolTipService.VerticalOffset="-75"
>
CurrentItem did not work for me either But @Nathtan's answer worked for my situation where I have a custom textBox resource. Thanks @Nathan I spent an hour on this.
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)/ErrorContent}" />
</Trigger>
</Style.Triggers>
I've seen the code you're using posted in multiple places, but it seems odd to me that
Path=(Validation.Errors)[0].ErrorContent}
doesn't raise any red flags. But I'm also new to WPF and perhaps there is some secret to making that work in every case.
Rather than attempting to index a possibly empty collection with an array index, add a converter that returns the first error in the list.
精彩评论