I have a WPF application that uses the ObservableValidator to handle validation of properties using data annotations. This works great for string properties. For example:
public class LoginViewModel : ObservableValidator
{
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[StringLength(64, MinimumLength = 8)]
private string username = String.Empty;
[RelayCommand]
private void LogIn()
{
ValidateAllProperties();
if(HasErrors)
{
return;
}
// Log in the user
}
// ...
}
When I bind the Username property to a text box using <TextBox Text="{Binding Username, ValidatesOnNotifyDataErrors=True}" />, I automatically get validation messages in my view that appear around the textbox!
However, I don't know how I'm supposed to handle cases where I need to validate non-string properties. For example:
public class User : ObservableValidator
{
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[Range(0, 200)]
private int age = 10;
}
If I used the same approach of <TextBox Text="{Binding Age, ValidatesOnNotifyDataErrors=True}" /> and the user enters a value that's not an integer like "12aaa", the default value converter on the binding throws an exception saying that "12aaa" cannot be converted into an integer. These value conversion exceptions can't be detected from my view model since the binding engine never updates the property value.
Thus, calling ValidateAllProperties() sets HasError to false, even though the user entered invalid data!
I see a few ways to handle these impossible-to-detect errors:
- Prevent the user from ever entering invalid data. This seems feasible at first, but becomes harder with more complicated types (e.g., a user input for a
TimeSpan). - Add string fields for each of the non-string properties. Even if you do this, I don't know how to propagate validation errors from the typed properties to the non-typed counterparts so they show up around the appropriate text box.
- Probably some other options I haven't thought of.
Are there any recommended ways of handling conversion errors for non-string properties with the MVVM Toolkit's ObservableValidator? Thanks in advance for your help!
This is a runtime exception and not thrown by the library you use. The exception is thrown before the value is assigned to the source property (when the binding engine tries to convert from target type
stringto the source typeint). The exception is not related to data validation.TextBox.Textis of typestring.User.Ageis of typeint. The exception is thrown because the binding tries to assign astringvalue from theTextBoxto anintproperty. There is no implicit cast fromstringtoint. In case the source property type differs from the target property type (and there is no implicit type conversion)Bindingmarkup extension will use a default converter so that e.g. anintvalue can be send/assigned to astringbinding target property. But this only works if there exists a default conversion.For example,
"123"is purely numeric and can be converted tointusing a default converter. But"123abc"is alphanumeric and the standard conversion tointfails and the binding engine throws the exception.You can:
User.Ageof typestringUser.AgeTextproperty of typestringto bind to theTextBoxand convert it tointin your data model for example usingint.TryParsefrom the property setterIValueConverterto convert the binding target value fromstringtointe.g. with the help ofint.TryParse.TextBoxand implement an e.g.NumericTextBoxwhere the input is converted fromstringtointor the input is validated to ensure a valid numeric value (so that the default converter can handle it). The validation of the value itself (e.g. if the numeric value is within a particular range) is still implemented in the data source (view model class)