Question

I am designing a WPF application following MVVM. My ViewModel is exposing one Double property called DoubleValue, which is binding to a TextBox in the View. I have set "ValidatesOnDataErrors=True" for the binding. So if the user types a string which can't be converted to a Double, it display the red background.

In my ViewModel I also have a Command object, let's call SaveCommand, whose CanExecute delegate is depending on whether there is any error in the VM (my ViewModelBase class implements IDataErrorInfo, I have an overridable ValidatePropertyByName function and the validation errors are stored in a dictionary.) But now my problem is, if I give an invalid string in the TextBox, since the conversion fails, it never calls the setter of the binding property value. In another word, the ValidatePropertyByName is not called and the error dictionary remains the previous state, which normally is clean. So if now the user click the Save button (which is enabled since the error dictionary is clean), the SaveCommand executes with the previous valid double value to save. This is obviously not good.

So how can I make my ViewModel aware of such conversion errors?

UPDATE:

Some code example:

The binding property is like this:

Public Property DoubleValue As Double
        Get
            Return _doubleValue
        End Get
        Set(value As Double)
            If value <> _doubleValue Then
                _doubleValue = value
                RaisePropertyChanged("DoubleValue")
            End If
        End Set
    End Property
    Private _doubleValue As Double

My binding is like this:

<TextBox Grid.Row="3" Text="{Binding DoubleValue, ValidatesOnDataErrors=True}" />

And now my problem is: if I give a string "XXX" in the text box, since it can't be converted to a double value, the setter of DoubleValue is never get called. And so the property value remains the previous(valid) value. Now if my SaveCommand gets executed, it will do the save operation with this previous valid value, which will make the user confused.

Was it helpful?

Solution 2

You cannot simply put these two things together. One is the regular validation inside the ViewModel. The other are control-specific problems, like unconvertible values.

So there are two possible ways to solve this:

1) Don't use a converter. Just bind the string. Inside the ViewModel you can then use the validation to check for a valid value. (More MVVM)

2) Store your ValidationErrors on the controlside and merge them with the viewmodel errors. This is not easy but a good way to create one source for binding against ALL problems within your UI. We are doing this for complex textboxes at work. This means manual code in the controls but for complex customcontrols this is OK, I believe.

edit: just to elaborate a little on the 2nd point. We are having a DependencyProperty of Type ObservableCollection inside the Control. Then you can bind this Collection to a ViewModel Property and as soon as your control moves an Error inside the collection it is available inside the viewModel. You can then use this collection inside your validation implementation. This works pretty well for larger controls.

Edit2: For the MarkInvalid Stuff I mentioned in the comment. It would look like this:

DataErrorValidationRule validationRule = new DataErrorValidationRule();
ValidationError validationError = new ValidationError(validationRule, myTextBox.GetBindingExpression(TextBox.TextProperty)) { ErrorContent = "My custom message" };
Validation.MarkInvalid(myTextBox.GetBindingExpression(TextBox.TextProperty), validationError);

You would call in from inside a TextChanged when you can't convert the new given value or

Validation.ClearInvalid(myTextBox.GetBindingExpression(TextBox.TextProperty))

Maybe that will help?

OTHER TIPS

the most easy way is to just use string properties in your viewmodel. then you get all input from the user and can validate it in your viewmodel. the drawback is that you have to convert the values to the right type when you go to the model.

if you dont want this you have to create your own controls or better behaviors so that the use can just input values that your viewmodel expect. eg. NumericInputBehavior.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top