Reputation: 3774
My view model base class was originally implementing INotifyDataErrorInfo, and everything worked flawlessly, but I am now exploring how I might do validation using composition rather than inheritance, so that my base view model class doesn't have to do anything more than INotifyPropertyChanged. I'm also looking for a reusable solution so I don't have to implement INotifyDataErrorInfo on all my view models.
I created a concrete implementation of INotifyDataErrorInfo that I can include in my view models that need validation (only relevant code included):
public class NotifyDataErrorInfo : INotifyDataErrorInfo
{
public readonly Dictionary<string, string> ValidationErrorsByPropertyName = new Dictionary<string, string>();
public IEnumerable GetErrors(string propertyName)
{
...
}
public bool HasErrors { get; }
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
When MyViewModel has validation errors, it gets/sets them through an instance of the NotifyDataErrorInfo object. (In my original version, ViewModel implemented INotifyDataErrorInfo. This is no longer the case as I explore achieving the same results via composition.)
public class MyViewModel : ViewModel
{
public NotifyDataErrorInfo NotifyDataErrorInfo { get; } = new NotifyDataErrorInfo();
}
Here is a text box that is reporting validation errors on the MaxDaysText property setter, and sets the validation error template.
<TextBox
Text="{Binding MaxDaysText, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{StaticResource TextBoxValidationErrorTemplate}" />
I now need to update my validation error template to access the errors in the NotifyDataErrorInfo property but I am not sure how to do this.
<ControlTemplate x:Key="TextBoxValidationErrorTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Border
Grid.Row="0"
HorizontalAlignment="Left"
BorderBrush="{StaticResource ErrorMessageBorderBrush}"
BorderThickness="1">
<AdornedElementPlaceholder x:Name="_adornedElementPlaceholder" />
</Border>
<ItemsControl
Grid.Row="1"
ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox
Style="{StaticResource ErrorMessageStyle}"
Text="{Binding Path=ErrorContent, Mode=OneWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ControlTemplate>
I have tried changing all the bindings to look for NotifyDataErrorInfo, but with no luck. What change do I need to make to the template to access the validation errors on MyViewModel's NotifyDataErrorInfo property?
EDIT: It appears that in the composition approach, ErrorsChanged is always null, and the view is never being notified. I guess when the view model itself implements INotifyDataErrorInfo, the framework assigns the delegate using ErrorsChangedEventManager. But now I'm leaving that out of the loop. So it appears that composition will not work for this approach. Is this assessment correct?
Upvotes: 0
Views: 783
Reputation: 104
The property is called ‘NotifyDataErrorInfo’, you need to bind ItemsSource to this property
<ItemsControl
Grid.Row="1"
ItemsSource="{Binding NotifyDataErrorInfo}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox
Style="{StaticResource ErrorMessageStyle}"
Text="{Binding Path=ErrorContent, Mode=OneWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If the controltemplate doesn’t retrieve the datacontext, then you need to add it
<ControlTemplate DataContext={Binding DataContext,
RelativeSource={RelativeSource AncestorType={x:Type views:YourView}}} x:Key="TextBoxValidationErrorTemplate">
Upvotes: 0