It is a small library for ReactiveUI to support XAML Binding validation using *FluentValidation
- ReactiveUI is one of the most powerful framworks for MVVM.
- It has a original validation library (ReactiveUI.Validation), It is very simple to use and well functioned.
- But It does not support
INotifyDataErrorInfo
interface for XAML binding engine to display validation erros in your view. - So, I wrap the base ViewModel (ReactiveObject) with a popular validation library *FluentValidation
- To XAML binding, we should initialize the DataContext of View. Andthen we use some traditional XAML markup bindings (only some properties that we want to validate).
CAUTION
If you HATE to use XAML markup bindings. It's NOT for you.
ReactiveValidationObject can be installed using the Nuget package manager or the dotnet CLI:
Install-Package ReactiveUI-FluentValidation
Or you can just copy the ReactiveValidationObject.cs
into your project.
After that you should get *FluentValidation pakage.
I use a popular validation library *FluentValidation. Therefore you can learn more validation rules HERE - https://fluentvalidation.net/
So this thime. I just show a how to use my library only.
This is our first ReactiveObject ViewModel class:
using ReactiveUI;
public class AppViewModel : ReactiveObject
{
private string fullname;
private string surname;
private string forename;
public string Fullname
{
get => fullname;
set
{
this.RaiseAndSetIfChanged(ref fullname, value);
}
}
public string Surname
{
get => surname;
set
{
this.RaiseAndSetIfChanged(ref surname, value);
}
}
public string Forename
{
get => forename;
set
{
this.RaiseAndSetIfChanged(ref forename, value);
}
}
public ReactiveCommand<Unit, Unit> SomeAction { get; }
public AppViewModel()
{
var isValid = this.WhenAnyValue(vm => vm.Surname, vm => vm.Forename)
.Select(x => !String.IsNullOrWhiteSpace(x.Item1)
&& !String.IsNullOrWhiteSpace(x.Item2));
SomeAction = ReactiveCommand.Create(() =>
{
Fullname = $"{Forename} {Surname}";
}, isValid);
}
}
As you can see it is work very well.
You can not click the button if suname and forename are empty:
However If you wanna display validation erros in your view.
Follow next step.
You would define a set of validation rules for our first class by inheriting from AbstractValidator<T>
:
using FluentValidation;
public class AppViewModelValidator : AbstractValidator<AppViewModel>
{
public AppViewModelValidator()
{
RuleFor(vm => vm.Surname).NotEmpty();
RuleFor(vm => vm.Forename).NotEmpty();
}
}
First of all we should change base class.
Before:
public class AppViewModel : ReactiveObject
After:
using ReactiveUI.FluentValidation;
public class AppViewModel : ReactiveValidationObject
And then you have to set an argument AbstractValidator<AppViewModel>
to the base class constructor:
public AppViewModel() : base(new AppViewModelValidator())
{
SomeAction = ReactiveCommand.Create(() =>
{
Fullname = $"{Forename} {Surname}";
}, isValid);
}
You now don't need define IObservable<bool> isValid
any more. The validation result isValid
will be set if validations are completed.
Finally, You should raise an event related property this.RaiseValidation(nameof(Property))
:
public string Surname
{
get => surname;
set
{
this.RaiseAndSetIfChanged(ref surname, value);
this.RaiseValidation(nameof(Surname));
}
}
public string Forename
{
get => forename;
set
{
this.RaiseAndSetIfChanged(ref forename, value);
this.RaiseValidation(nameof(Forename));
}
}
So, This is our first ReactiveValidationObject ViewModel class:
using ReactiveUI;
using ReactiveUI.FluentValidation;
using FluentValidation;
public class AppViewModelValidator : AbstractValidator<AppViewModel>
{
public AppViewModelValidator()
{
RuleFor(vm => vm.Surname).NotEmpty();
RuleFor(vm => vm.Forename).NotEmpty();
}
}
public class AppViewModel : ReactiveValidationObject
{
private string fullname;
private string surname;
private string forename;
public string Fullname
{
get => fullname;
set
{
this.RaiseAndSetIfChanged(ref fullname, value);
}
}
public string Surname
{
get => surname;
set
{
this.RaiseAndSetIfChanged(ref surname, value);
this.RaiseValidation(nameof(Surname));
}
}
public string Forename
{
get => forename;
set
{
this.RaiseAndSetIfChanged(ref forename, value);
this.RaiseValidation(nameof(Forename));
}
}
public ReactiveCommand<Unit, Unit> SomeAction { get; }
public AppViewModel() : base(new AppViewModelValidator())
{
SomeAction = ReactiveCommand.Create(() =>
{
Fullname = $"{Forename} {Surname}";
}, isValid);
}
}
As I mentioned our goal is to display errors in your view. To use XAML binding engine set DataContext:
DataContext = ViewModel;
And then you should bind properties in xaml:
<TextBox x:Name="TextboxSurname"
Text="{Binding Surname, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox x:Name="TextboxForename"
Text="{Binding Forename, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Now you can see errors in your view like this: