Skip to content

Commit

Permalink
Support INotifyDataErrorInfo, rename ISupportsValidation (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
worldbeater authored and glennawatson committed Sep 21, 2019
1 parent 88ca762 commit dba5277
Show file tree
Hide file tree
Showing 15 changed files with 394 additions and 64 deletions.
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ Install the following package into you class library and platform-specific proje

## How to use

* For ViewModels which need validation, implement `ISupportsValidation`.
* For ViewModels which need validation, implement `IValidatableViewModel`.
* Add validation rules to the ViewModel.
* Bind to the validation rules in the View.

## Example

1. Decorate existing ViewModel with `ISupportsValidation`, which has a single member, `ValidationContext`. The ValidationContext contains all of the functionality surrounding the validation of the ViewModel. Most access to the specification of validation rules is performed through extension methods on the ISupportsValidation interface. Then, add validation to the ViewModel.
1. Decorate existing ViewModel with `IValidatableViewModel`, which has a single member, `ValidationContext`. The ValidationContext contains all of the functionality surrounding the validation of the ViewModel. Most access to the specification of validation rules is performed through extension methods on the `IValidatableViewModel` interface. Then, add validation to the ViewModel.

```csharp
public class SampleViewModel : ReactiveObject, ISupportsValidation
public class SampleViewModel : ReactiveObject, IValidatableViewModel
{
public SampleViewModel()
{
// Creates the validation for the Name property
// Creates the validation for the Name property.
this.ValidationRule(
viewModel => viewModel.Name,
name => !string.IsNullOrWhiteSpace(name),
Expand Down Expand Up @@ -69,7 +69,7 @@ public class SampleView : ReactiveContentPage<SampleViewModel>
.DisposeWith(disposables);

// Bind any validations which reference the Name property
// to the text of the NameError UI control
// to the text of the NameError UI control.
this.BindValidation(ViewModel, vm => vm.Name, view => view.NameError.Text)
.DisposeWith(disposables);
});
Expand Down Expand Up @@ -115,6 +115,28 @@ namespace SampleApp.Activities
}
```

## INotifyDataErrorInfo Support

For those platforms which support the `INotifyDataErrorInfo` interface, ReactiveUI.Validation provides a helper base class named `ReactiveValidationObject<TViewModel>`. The helper class implements both the `IValidatableViewModel` interface and the `INotifyDataErrorInfo` interface. It listens to any changes in the `ValidationContext` and invokes `INotifyDataErrorInfo` events.

```cs
public class SampleViewModel : ReactiveValidationObject<SampleViewModel>
{
[Reactive]
public string Name { get; set; } = string.Empty;

public SampleViewModel()
{
this.ValidationRule(
x => x.Name,
name => !string.IsNullOrWhiteSpace(name),
"Name shouldn't be empty.");
}
}
```

> **Note** The `Reactive` attribute is from the [ReactiveUI.Fody](https://reactiveui.net/docs/handbook/view-models/boilerplate-code) NuGet package.
## Capabilities

1. Rules can be composed of single or multiple properties along with more generic Observables.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")]
namespace ReactiveUI.Validation.Abstractions
{
public interface ISupportsValidation
public interface IValidatableViewModel
{
ReactiveUI.Validation.Contexts.ValidationContext ValidationContext { get; }
}
Expand Down Expand Up @@ -50,6 +50,7 @@ namespace ReactiveUI.Validation.Components
public System.IObservable<ReactiveUI.Validation.States.ValidationState> ValidationStatusChange { get; }
protected void AddProperty<TProp>(System.Linq.Expressions.Expression<System.Func<TViewModel, TProp>> property) { }
public bool ContainsProperty<TProp>(System.Linq.Expressions.Expression<System.Func<TViewModel, TProp>> property, bool exclusively = False) { }
public bool ContainsPropertyName(string propertyName, bool exclusively = False) { }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
protected abstract System.IObservable<ReactiveUI.Validation.States.ValidationState> GetValidationChangeObservable();
Expand Down Expand Up @@ -104,13 +105,13 @@ namespace ReactiveUI.Validation.Extensions
public class static SupportsValidationExtensions
{
public static System.IObservable<bool> IsValid<TViewModel>(this TViewModel viewModel)
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule<TViewModel, TViewModelProp>(this TViewModel viewModel, System.Linq.Expressions.Expression<System.Func<TViewModel, TViewModelProp>> viewModelProperty, System.Func<TViewModelProp, bool> isPropertyValid, string message)
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule<TViewModel, TViewModelProp>(this TViewModel viewModel, System.Linq.Expressions.Expression<System.Func<TViewModel, TViewModelProp>> viewModelProperty, System.Func<TViewModelProp, bool> isPropertyValid, System.Func<TViewModelProp, string> message)
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule<TViewModel>(this TViewModel viewModel, System.Func<TViewModel, System.IObservable<bool>> viewModelObservableProperty, System.Func<TViewModel, bool, string> messageFunc)
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
}
public class static ValidationContextExtensions
{
Expand All @@ -129,16 +130,16 @@ namespace ReactiveUI.Validation.Extensions
public static System.IDisposable BindToDirect<TTarget, TValue>(System.IObservable<TValue> @this, TTarget target, System.Linq.Expressions.Expression viewExpression) { }
public static System.IDisposable BindValidation<TView, TViewModel, TViewModelProperty, TViewProperty>(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression<System.Func<TViewModel, TViewModelProperty>> viewModelProperty, System.Linq.Expressions.Expression<System.Func<TView, TViewProperty>> viewProperty)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static System.IDisposable BindValidation<TView, TViewModel, TViewProperty>(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression<System.Func<TView, TViewProperty>> viewProperty)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static System.IDisposable BindValidation<TView, TViewModel, TViewProperty>(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression<System.Func<TViewModel, ReactiveUI.Validation.Helpers.ValidationHelper>> viewModelHelperProperty, System.Linq.Expressions.Expression<System.Func<TView, TViewProperty>> viewProperty)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static System.IDisposable BindValidationEx<TView, TViewModel, TViewModelProperty, TViewProperty>(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression<System.Func<TViewModel, TViewModelProperty>> viewModelProperty, System.Linq.Expressions.Expression<System.Func<TView, TViewProperty>> viewProperty)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
}
}
namespace ReactiveUI.Validation.Formatters.Abstractions
Expand All @@ -159,6 +160,14 @@ namespace ReactiveUI.Validation.Formatters
}
namespace ReactiveUI.Validation.Helpers
{
public abstract class ReactiveValidationObject<TViewModel> : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel, System.ComponentModel.INotifyDataErrorInfo
{
protected ReactiveValidationObject(System.Reactive.Concurrency.IScheduler scheduler = null) { }
public bool HasErrors { get; }
public ReactiveUI.Validation.Contexts.ValidationContext ValidationContext { get; }
public event System.EventHandler<System.ComponentModel.DataErrorsChangedEventArgs> ErrorsChanged;
public virtual System.Collections.IEnumerable GetErrors(string propertyName) { }
}
public class ValidationHelper : ReactiveUI.ReactiveObject, System.IDisposable
{
public ValidationHelper(ReactiveUI.Validation.Components.Abstractions.IValidationComponent validation) { }
Expand Down Expand Up @@ -234,31 +243,31 @@ namespace ReactiveUI.Validation.ValidationBindings
public void Dispose() { }
public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty<TView, TViewModel, TViewModelProperty, TViewProperty>(TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, TViewModelProperty>> viewModelProperty, System.Linq.Expressions.Expression<System.Func<TView, TViewProperty>> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter<string> formatter = null, bool strict = True)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty<TView, TViewModel, TViewModelProperty, TOut>(TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, TViewModelProperty>> viewModelProperty, System.Action<ReactiveUI.Validation.States.ValidationState, TOut> action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter<TOut> formatter = null, bool strict = True)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForValidationHelperProperty<TView, TViewModel, TViewProperty>(TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, ReactiveUI.Validation.Helpers.ValidationHelper>> viewModelHelperProperty, System.Linq.Expressions.Expression<System.Func<TView, TViewProperty>> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter<string> formatter = null)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForValidationHelperProperty<TView, TViewModel, TOut>(TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, ReactiveUI.Validation.Helpers.ValidationHelper>> viewModelHelperProperty, System.Action<ReactiveUI.Validation.States.ValidationState, TOut> action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter<TOut> formatter = null)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForViewModel<TView, TViewModel, TOut>(TView view, System.Action<TOut> action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter<TOut> formatter)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForViewModel<TView, TViewModel, TViewProperty>(TView view, System.Linq.Expressions.Expression<System.Func<TView, TViewProperty>> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter<string> formatter = null)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
}
public sealed class ValidationBindingEx : ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding, System.IDisposable
{
public void Dispose() { }
public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty<TView, TViewModel, TViewModelProperty, TViewProperty>(TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, TViewModelProperty>> viewModelProperty, System.Linq.Expressions.Expression<System.Func<TView, TViewProperty>> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter<string> formatter = null, bool strict = True)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty<TView, TViewModel, TViewModelProperty, TOut>(TView view, System.Linq.Expressions.Expression<System.Func<TViewModel, TViewModelProperty>> viewModelProperty, System.Action<System.Collections.Generic.IList<ReactiveUI.Validation.States.ValidationState>, System.Collections.Generic.IList<TOut>> action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter<TOut> formatter = null, bool strict = True)
where TView : ReactiveUI.IViewFor<TViewModel>
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.ISupportsValidation { }
where TViewModel : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { }
}
}
Loading

0 comments on commit dba5277

Please sign in to comment.