Dror
Dror

Reputation: 2588

Validation between multiple fields in different levels

I have a problem with validations between multiple fields. For example, I have a ViewModel named RangeDateViewModel that contains 2 instances of a class named DateViewModel - they represent a start date and an end date respectively.

So my binding looks like this:

<TextBox Text="{Binding StartDate.Date, ValidateOnDataError=True}">
<TextBox Text="{Binding EndDate.Date, ValidateOnDataError=True}">

My RangeDateViewModel class implements the IDataErrorInfo interface. In my plan, the RangeDateViewModel would validate that the start date is before the end date, by applying the validation logic in the IDataErrorInfo["propertyName"] function like this:

public string this[string columnName]
{
     get
     {
        return ValidationError();
     }
}

The problem is that this is never being called, and instead the IDataErrorInfo properties that reside in each of the DateViewModel classes are being called instead.

I guess this is because the bound property is not in the same level of RangeDateViewModel, but instead inside the child DateViewModel.

I think my need is quite basic and there must be an easy solution for this problem.

I tried using ValidationRules instead of IDataErrorInfo but then I'd problems letting the ViewModel know of the current validation status from the ValidationRules.

Upvotes: 5

Views: 1511

Answers (2)

Daniel Hilgarth
Daniel Hilgarth

Reputation: 174467

Try using the following approach:

  1. Create a DataTemplate for DateViewModel:

    <DataTemplate DataType="{x:Type ViewModels:DateViewModel}">
        <TextBox Text="{Binding Date}">
    </DataTemplate>
    
  2. Bind the instances of this ViewModel to a ContentControl and set ValidateOnDataError to true on that binding:

    <ContentControl Content="{Binding StartDate, ValidateOnDataError=True}" />
    <ContentControl Content="{Binding EndDate, ValidateOnDataError=True}" />
    
  3. In RangeDateViewModel subscribe to the PropertyChanged event of StartDate and EndDate and when raised, raise a PropertyChanged event with StartDate / EndDate:

    StartDate.PropertyChanged += (s, e) => InvokePropertyChanged("StartDate");
    EndDate.PropertyChanged += (s, e) => InvokePropertyChanged("EndDate");
    

Upvotes: 1

Jens H
Jens H

Reputation: 4632

I had the problem that public string this[string columnName] was simply not called just the other week.

The solution was simple. The binding WPF binding engine could not follow the nesting of my ViewModels.

I had assumed that I needed to implement the property in the ViewModel that is the current DataContext, but instead it needs to be implemented in the ViewModel that is bound to the control.

Example:

<TextBox Text="{Binding Path=ProductViewModel.DescriptionViewModel.ProductName,
                                    Mode=TwoWay,
                                    ValidatesOnDataErrors=True,
                                    NotifyOnValidationError=True}" />

Here DescriptionViewModel is the class that contains the bound property. IDataErrorInfo needs to be implemented in that class (not in ProductViewModel or another class up the hierarchy that may contain it) then everything will work fine.

Upvotes: 1

Related Questions