Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon How can I use FluentValidation with MvvmCross to show errors in the UI


How can I use FluentValidation with MvvmCross to show errors in the UI


Using FluentValidation with MvvmCross to display errors in the UI involves several steps. While MvvmCross does not natively support `IDataErrorInfo` for binding error messages directly, you can still leverage FluentValidation's capabilities by implementing a custom approach to handle and display validation errors.

Step 1: Implement FluentValidation

First, you need to define validation rules using FluentValidation. This involves creating a validator class that inherits from `AbstractValidator` where `T` is your model or view model class.

csharp
using FluentValidation;

public class UserValidator : AbstractValidator
{
    public UserValidator()
    {
        RuleFor(u => u.Name).NotEmpty().WithMessage("Name is required.");
        RuleFor(u => u.Email).EmailAddress().WithMessage("Invalid email address.");
    }
}

Step 2: Validate in the ViewModel

In your view model, you can validate the data using the validator. You'll need to manually trigger validation when data changes.

csharp
using MvvmCross.ViewModels;
using System.Collections.Generic;

public class UserViewModel : MvxViewModel
{
    private User _user;
    private readonly UserValidator _validator;

    public UserViewModel(UserValidator validator)
    {
        _validator = validator;
        _user = new User();
    }

    public string Name
    {
        get => _user.Name;
        set
        {
            _user.Name = value;
            Validate();
            RaisePropertyChanged(() => Name);
        }
    }

    public string Email
    {
        get => _user.Email;
        set
        {
            _user.Email = value;
            Validate();
            RaisePropertyChanged(() => Email);
        }
    }

    private void Validate()
    {
        var result = _validator.Validate(_user);
        if (!result.IsValid)
        {
            // Handle validation errors
            HandleValidationErrors(result.Errors);
        }
    }

    private void HandleValidationErrors(List errors)
    {
        // Convert errors into a dictionary for easier binding
        var errorDictionary = errors.ToDictionary(e => e.PropertyName, e => e.ErrorMessage);

        // Update UI with error messages
        Errors = errorDictionary;
        RaisePropertyChanged(() => Errors);
    }

    public Dictionary Errors { get; private set; }
}

Step 3: Display Errors in the UI

To display errors in the UI, you can bind to the `Errors` dictionary in your view model. However, since MvvmCross does not support `IDataErrorInfo` directly, you might need to use a custom binding or a value converter to display error messages for specific fields.

For example, you can create a custom binding to display error messages:

csharp
var set = this.CreateBindingSet();
set.Bind(errorLabel).To(vm => vm.Errors["Name"]);

Alternatively, you can use a value converter to convert the error message into a visible or invisible state for an error label.

Step 4: Implement Custom Binding or Converter

If you prefer a more dynamic approach, consider implementing a custom binding or a value converter that can handle displaying error messages based on the validation results.

csharp
public class ErrorVisibilityConverter : IMvxValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is string errorMessage && !string.IsNullOrEmpty(errorMessage))
        {
            return Visibility.Visible;
        }
        return Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Then, use this converter in your binding:

csharp
set.Bind(errorLabel).For(v => v.Visibility).To(vm => vm.Errors["Name"]).WithConversion(new ErrorVisibilityConverter());

Conclusion

While MvvmCross does not directly support `IDataErrorInfo`, you can effectively use FluentValidation by manually handling validation errors in your view model and displaying them in the UI using custom bindings or value converters. This approach allows you to leverage the power of FluentValidation's fluent interface for defining validation rules while still providing a seamless user experience with error feedback in your MvvmCross application.

Citations:
[1] https://brettedotnet.wordpress.com/2011/10/14/idataerrorinfo-and-fluentvalidation/
[2] https://stackoverflow.com/questions/17534463/using-mvvmcross-to-bind-to-error-messages
[3] https://www.mvvmcross.com/documentation/advanced/customizing-using-App-and-Setup
[4] https://www.youtube.com/watch?v=vaDDB7BpEgQ
[5] https://stackoverflow.com/questions/48066268/xamarin-mvvmcross-fluent-binding-on-uwp/55930449
[6] https://github.com/JeremySkinner/FluentValidation/issues/267
[7] https://www.mvvmcross.com/documentation/fundamentals/data-binding
[8] https://www.milanjovanovic.tech/blog/cqrs-validation-with-mediatr-pipeline-and-fluentvalidation
[9] https://stackoverflow.com/questions/31695804/wpf-mvvm-with-mvvmlight-and-fluent-validation
[10] https://www.thetechgrandma.com/2017/05/wpf-prism-inotifydataerrorinfo-and.html
[11] https://xamarinmonkeys.blogspot.com/2021/10/xamarinforms-validation-using-fluent.html
[12] https://www.codeproject.com/Articles/1069135/Dynamic-Validation-with-FluentValidation-in-WPF-MV