4
votes

I have two controls bound to properties MinCartValue and MaxCartValue. MinCartValue must be less than MaxCartValue. To achieve this validation I have implemented the the IDataErrorInfo interface, and run the above check in the this[columnName] method if either MinCartValue or MaxCartValue are touched. ValidatesOnDataErrors=True is set in the binding of both controls. The validation works correctly, highlighting each control when a change to its property value violates the rule. The problem is that once a control is flagged as invalid, if the user corrects the problem by altering the other control's value, the first control remains flagged as invalid. This is understandable because the IDataErrorInfo method was not doing validation on the first control's property.

So what I need is a way to force property #1 to be re-validated (or a way to clear the invalid state) when property #2 is validated, and vice versa. I have tried calling RaisePropertyChanged within my this[columnName] method but it does nothing. Also tried setting the property to its own value to try to trick it to validate itself, but again nothing happens.

Thanks

4

4 Answers

2
votes

I would recommend looking at the INotifyDataErrorInfo interface (introduced in Silverlight 4). It's able to async-notify if properties become invalid, so I think the framework is better about respecting this across many properties instead of expecting that the property currently being changed is the only one whose validity may be changing.

1
votes

I had two DateTime properties (DateFrom and DateTo) that needed to be validated against each other. In the setters for these properties I just raised a PropertyChanged event for both DateTo and DateFrom. Worked like a charm.

0
votes

I'm not sure if I'm understanding your problem exactly, but perhaps this may help. Providing some example XAML and the binding property code would help.

It sounds like an issue of your code depending on the default UpdateSourceTrigger, which in the case of TextBox controls is their focus/unfocus. You can set in the XAML the UpdateSourceTrigger attribute by adding UpdateSourceTrigger=Explicit to your binding where your validation occurs. Then in each TextBox (MinCartValue, MaxCartValue), add an event handler to the TextChanged event.

In the code-behind in the event handler, you can do something like this:

    private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        TheTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
    }

TheTextBox in this case would be one of your cart controls. The UpdateSource() method is a way to manually update the binding value, which should trigger your validation. This method provides away to tie into a trigger to update values and raising properties have changed outside of the default scope (using text changed intead of focus and unfocus on TextBox in this instance).

0
votes

Here's how I solved it. Let's say Property1 and Property2 are codependent. I'm not familiar with MVVM (yet), but you're probably extending your entity class to implement IDataErrorInfo. In this case you can also extend On[Property]Changed method and report change in codependent property:

partial class YourEntity : IDataErrorInfo
{
    public string this[string columnName]
        {
            //Your validation logic
        }

    public string Error
    {
        //WPF doesn't use it anyway
        get { return string.Empty; }
    }

    partial void OnProperty1Changed() 
    {
        OnPropertyChanging("Property2");
        OnPropertyChanged("Property2");
    }

    partial void OnProperty2Changed()
    {
        OnPropertyChanging("Property1");
        OnPropertyChanged("Property1");
    }
}

In this case the update in either one of this properties makes both bound controls re-evaluate themselves.

EDIT2: It appears that you should use OnPropertyChang* instead of ReportPropertyChang*. ReportPropertyChanged will notify the entity framework that there are pending changes in the model, but in fact all you're trying to do is inform the view. You don't want to update the database with the property that didn't really change. ReportPropertyChang* will also fail on computed fields that have no mappings in the database. EDIT1: Found out that it's essential to call ReportPropertyChanging before ReportPropertyChanged.