WPF with MVVM and DataAnnotations, validation errors in UserControl

I have a UserControl that will be reused throughout the entire application that we are developing. We use a framework based on MVVMLight.

For simplicity, we can say that a user control contains only one text field and provides one dependency property called Quantity. A text field in a user control is a data binding to the Quantity dependency property.

When a user control is used in a view, the Quantity control dependency property of the user control is a binding to a property binding in the ViewModel. (This ViewModel is the datacontext of our view using MVVMLight ViewModelLocator).

It all works great! Bindings work, properties are set as I expected. All is well until it comes to confirmation.

We use DataAnnotations to decorate our ViewModel properties. ViewModel contains a custom implementation of INotifyDataErrorInfo. We have implemented custom styles for most input controls to show a red border around the control, and a message next to the control that displays a validation error message. All this works fine in the normal case (for example, a text field in a view associated with a property in the view model).

When I try to use the same approach with this user control, what I get is a red frame around the entire user control and no error displaying in the actual text box. It seems that the fact that there is an error is reflected in the user interface, but it just does not bring it to the control in which I want.

I searched in stackoverflow for this problem, from these issues with solutions, none seem to work for my situation.

My first assumption is that since the actual text field is bound directly to the dependency property, and not to the property in my view model, it is not properly notified of errors that have occurred. Is there a way to propagate these errors generated in the viewmodel via usercontrol and then to the text box?

Any help or suggestions you can give would be great, thanks.

Here is the xaml UserControl.

<UserControl x:Class="SampleProject.UserControls.SampleControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" x:Name="sampleControl" d:DesignHeight="300" d:DesignWidth="300"> <Grid x:Name="LayoutRoot" DataContext="{Binding ElementName=sampleControl}"> <TextBox Text="{Binding Path=Quantity, ValidatesOnDataErrors=True}" Width="100" Height="30" /> </Grid> </UserControl> 

UserControl code behind.

 public partial class SampleControl : UserControl { public SampleControl() { InitializeComponent(); } public static readonly DependencyProperty QuantityProperty = DependencyProperty.Register("Quantity", typeof(int?), typeof(SampleControl), new FrameworkPropertyMetadata{DefaultValue=null, BindsTwoWayByDefault = true}); public int? Quantity { get { return (int?)GetValue(QuantityProperty); } set { SetValue(QuantityProperty, value); } } } 

Used to view.

 <userControls:SampleControl Grid.Row="1" Quantity="{Binding Path=Quantity, ValidatesOnDataErrors=True}" Height="60" Width="300"/> 

ViewModel property.

 [Required(ErrorMessage = "Is Required")] [Range(5, 10, ErrorMessage = "Must be greater than 5")] public int? Quantity { get { return _quantity; } set { Set(() => Quantity, ref _quantity, value); } } private int? _quantity; 

(* Note: The Set method in setter is only a helper method in the base view model, which sets the backing property and raises the PropertyChanged event for it.)

+6
source share
1 answer

Try removing the DataContext from the UserControl . Instead, Bind directly from the TextBox to the actual property using the RelativeSource Binding :

 <TextBox Text="{Binding Quantity, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type YourControlNamespace:SampleControl, ValidatesOnDataErrors=True}}}" Width="100" Height="30" /> 

UPDATE β†’>

If this is not the case, if the view models bound to this property will always have a property with the same name that you can associate with, you can get this Binding to search through the DataContext parents as follows:

 <TextBox Text="{Binding Quantity, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=2, ValidatesOnDataErrors=True}}}" Width="100" Height="30" /> 

You will need to change 2 as the correct number of parent elements, which is the TextBox value, before reaching the control with access to the correct property. For example, using level 2 means that the Framework will try to find a property called Quantity to Bind in the DataContext parent TextBox control. It’s harder to get this working with AncestorLevel , although I believe that β€œhidden” elements like Grid are not included as parents.

0
source

Source: https://habr.com/ru/post/955376/


All Articles