ReactiveUI.Validation
June 3, 2026 · View on GitHub
ReactiveUI.Validation
Validación para soluciones basadas en ReactiveUI, funcionando de manera reactiva.
Este documento es una traducción al español de README.md. Si encuentras alguna discrepancia, el README en inglés es la referencia autoritativa.
Equipo principal
Chris Pulman Londres, Reino Unido |
Glenn Watson Melbourne, Australia |
Historia y contribuidores anteriores
ReactiveUI.Validation fue desarrollado originalmente por @jcmm33 como Vistian.Reactive.Validation, y posteriormente refactorizado y actualizado por Àlex Martínez Morón y la comunidad de ReactiveUI.
Plataformas compatibles
| Plataforma | Targets |
|---|---|
| .NET | net8.0, net9.0, net10.0 |
| .NET Framework | net462, net472, net481 |
| Windows | net8.0-windows10.0.19041.0, net9.0-windows10.0.19041.0, net10.0-windows10.0.19041.0 |
| Android (MAUI) | net9.0-android, net10.0-android |
Paquetes NuGet
Instala los siguientes paquetes en tu biblioteca de clases y en el proyecto específico de la plataforma.
| Plataforma | Paquete | NuGet |
|---|---|---|
| Cualquier plataforma | ReactiveUI.Validation | |
| AndroidX (MAUI) | ReactiveUI.Validation.AndroidX |
Cómo usarlo
- Para los ViewModels que necesiten validación, implementa
IValidatableViewModel. - Añade reglas de validación al ViewModel usando los métodos de extensión
ValidationRule. - Vincula las reglas de validación en la vista mediante
BindValidationoINotifyDataErrorInfo.
Ejemplo
- Decora un ViewModel existente con
IValidatableViewModel, que tiene un único miembro:ValidationContext. ElValidationContextcontiene toda la funcionalidad relacionada con la validación del ViewModel. La mayor parte del acceso a la especificación de reglas de validación se realiza a través de métodos de extensión sobre la interfazIValidatableViewModel. Luego, agrega la validación al ViewModel.
using ReactiveUI.Validation.Extensions;
public class SampleViewModel : ReactiveObject, IValidatableViewModel
{
public SampleViewModel()
{
// Crea la validación para la propiedad Name.
this.ValidationRule(
viewModel => viewModel.Name,
name => !string.IsNullOrWhiteSpace(name),
"Debes especificar un nombre válido");
}
public ValidationContext ValidationContext { get; } = new ValidationContext();
private string _name;
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
}
Para escenarios de validación más complejos existen varias sobrecargas adicionales del método de extensión ValidationRule que aceptan observables. Estas permiten que la validación ocurra de forma asíncrona, y permiten combinar cadenas complejas de observables para producir resultados de validación.
La sobrecarga más simple acepta un IObservable<bool> donde el booleano observado indica si la ValidationRule es válida o no. Esta sobrecarga acepta un mensaje que se utiliza cuando el observable produce un resultado false (inválido).
IObservable<bool> passwordsObservable =
this.WhenAnyValue(
x => x.Password,
x => x.ConfirmPassword,
(password, confirmation) => password == confirmation);
this.ValidationRule(
vm => vm.ConfirmPassword,
passwordsObservable,
"Las contraseñas deben coincidir.");
Cualquier observable existente puede usarse para alimentar una ValidationRule mediante la sobrecarga del método de extensión que acepta un flujo arbitrario IObservable<TState>. La sobrecarga acepta una función de validación personalizada que recibe el último TState, y una función de mensaje de error personalizada, responsable de formatear el último objeto TState. La sintaxis se ve así:
// IObservable<{ Password, Confirmation }>
var passwordsObservable =
this.WhenAnyValue(
x => x.Password,
x => x.ConfirmPassword,
(password, confirmation) =>
new { Password = password, Confirmation = confirmation });
this.ValidationRule(
vm => vm.ConfirmPassword,
passwordsObservable,
state => state.Password == state.Confirmation,
state => $"Las contraseñas deben coincidir: {state.Password} != {state.Confirmation}");
Nota: La función para extraer un mensaje (
messageFunc) solo se invoca si la función que establece la validez (isValidFunc) devuelvefalse; en caso contrario, el mensaje se establece comostring.Empty.
Finalmente, puedes proporcionar directamente un observable que emita cualquier objeto (o struct) que implemente IValidationState; o puedes usar la clase base ValidationState, que ya implementa la interfaz. Como el objeto resultante se almacena directamente en el contexto sin transformación adicional, este puede ser el enfoque más eficiente:
IObservable<IValidationState> usernameNotEmpty =
this.WhenAnyValue(x => x.UserName)
.Select(name => string.IsNullOrEmpty(name)
? new ValidationState(false, "El nombre de usuario no debe estar vacío")
: ValidationState.Valid);
this.ValidationRule(vm => vm.UserName, usernameNotEmpty);
Nota: Dado que un
ValidationStateválido no requiere realmente un mensaje, existe una propiedad singletonValidationState.Validque se recomienda utilizar siempre que sea posible para indicar un estado válido, reduciendo así las asignaciones de memoria.
- Añade la presentación de la validación a la vista.
using ReactiveUI.Validation.Extensions;
public class SampleView : ReactiveContentPage<SampleViewModel>
{
public SampleView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.Name, view => view.Name.Text)
.DisposeWith(disposables);
// Vincula cualquier validación que referencie la propiedad Name
// al texto del control de UI NameError.
this.BindValidation(ViewModel, vm => vm.Name, view => view.NameError.Text)
.DisposeWith(disposables);
// Vincula cualquier validación asociada a este ViewModel concreto
// al texto del control de UI FormErrors.
this.BindValidation(ViewModel, view => view.FormErrors.Text)
.DisposeWith(disposables);
});
}
}
Ejemplo con extensiones de Android
Existen métodos de extensión para Android y su control de Material Design TextInputLayout. Estas extensiones utilizan internamente la propiedad Error del control TextInputLayout, permitiendo implementar un comportamiento totalmente nativo para mostrar errores de validación. Para usar estas extensiones debes importar ReactiveUI.Validation.Extensions e instalar ReactiveUI.Validation.AndroidX:
dotnet add package ReactiveUI.Validation.AndroidX
// Esta directiva using hace disponibles las extensiones específicas de Android.
using ReactiveUI.Validation.Extensions;
public class SignUpActivity : ReactiveAppCompatActivity<SignUpViewModel>
{
// Los cuadros de texto nativos de Android declarados en un archivo .axml.
public TextInputEditText Password { get; set; }
public TextInputEditText ConfirmPassword { get; set; }
// Los layouts que envuelven los cuadros de texto declarados en un archivo .axml.
public TextInputLayout PasswordField { get; set; }
public TextInputLayout ConfirmPasswordField { get; set; }
protected override void OnCreate (Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
// El método WireUpControls es un método utilitario de ReactiveUI para Android, ver:
// https://www.reactiveui.net/docs/handbook/data-binding/xamarin-android/wire-up-controls
this.WireUpControls();
this.Bind(ViewModel, x => x.Password, x => x.Password.Text);
this.Bind(ViewModel, x => x.ConfirmPassword, x => x.ConfirmPassword.Text);
// Vincula cualquier validación que referencie la propiedad Password
// a la propiedad Error del control TextInputLayout.
this.BindValidation(ViewModel, x => x.Password, PasswordField);
this.BindValidation(ViewModel, x => x.ConfirmPassword, ConfirmPasswordField);
}
}
Soporte para INotifyDataErrorInfo
Para aquellas plataformas que admiten la interfaz INotifyDataErrorInfo, ReactiveUI.Validation proporciona una clase base auxiliar llamada ReactiveValidationObject. Esta clase auxiliar implementa tanto la interfaz IValidatableViewModel como la interfaz INotifyDataErrorInfo. Escucha cualquier cambio en el ValidationContext e invoca los eventos de INotifyDataErrorInfo.
using ReactiveUI.Validation.Extensions;
public class SampleViewModel : ReactiveValidationObject
{
public SampleViewModel()
{
this.ValidationRule(
viewModel => viewModel.Name,
name => !string.IsNullOrWhiteSpace(name),
"El nombre no debe ser nulo ni estar en blanco.");
}
private string _name = string.Empty;
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
}
Nota: Ten en cuenta que
INotifyDataErrorInfosolo se admite mediante el binding de XAML. El binding de ReactiveUI no utiliza las clases integradas de WPF.
Cuando uses una sobrecarga de ValidationRule que acepte un observable, recuerda proporcionar como primer argumento la propiedad a la que se dirige la regla de validación. De lo contrario, INotifyDataErrorInfo no puede determinar a qué propiedad pertenece el mensaje de error.
this.ValidationRule(
vm => vm.ConfirmPassword,
passwordsObservable,
"Las contraseñas deben coincidir.");
Formateadores personalizados
Puedes pasar una instancia de IValidationTextFormatter<T> a una llamada de BindValidation si deseas reemplazar el SingleLineFormatter predeterminado utilizado en la biblioteca de validación. SingleLineFormatter acepta un carácter separador y usa un espacio en blanco por defecto, así que el siguiente fragmento muestra cómo usar un separador no predeterminado:
// Este formateador se basa en el SingleLineFormatter predeterminado pero usa un separador personalizado.
var formatter = new SingleLineFormatter(Environment.NewLine);
this.BindValidation(ViewModel, x => x.ErrorLabel.Text, formatter)
.DisposeWith(disposables);
La implementación personalizada más simple posible de IValidationTextFormatter<TOut> podría verse así:
private class ConstFormatter : IValidationTextFormatter<string>
{
private readonly string _text;
public ConstFormatter(string text = "La entrada es inválida.") => _text = text;
public string Format(ValidationText validationText) => _text;
}
// Este formateador se basa en una implementación personalizada de IValidationTextFormatter.
var formatter = new ConstFormatter("La entrada es inválida.");
this.BindValidation(ViewModel, x => x.ErrorLabel.Text, formatter)
.DisposeWith(disposables);
Si deseas reemplazar el IValidationTextFormatter<string> utilizado por defecto en ReactiveUI.Validation, registra una instancia de IValidationTextFormatter<string> en Locator.CurrentMutable antes de que tu aplicación inicie. Esto puede ser útil cuando tu aplicación necesita localización y deseas pasar claves de mensajes en lugar de mensajes a las llamadas de ValidationRule.
// Registra una instancia singleton de IValidationTextFormatter<string> en Splat.Locator.
Locator.CurrentMutable.RegisterConstant(new CustomFormatter(), typeof(IValidationTextFormatter<string>));
Capacidades
En esencia, ReactiveUI.Validation es un modelo relativamente simple del ValidationContext que contiene una lista de instancias IValidationComponent. Un IValidationComponent provee un observable de IValidationState. Cada vez que el estado de validación cambia (ya sea una transición de validez) o cambia el ValidationText, se emite un nuevo valor.
- Las reglas pueden componerse de una o varias propiedades junto con observables más genéricos.
- El texto de validación puede encapsular tanto estados válidos como inválidos.
- La vinculación puede realizarse a una vista o a una acción.
- El texto de validación puede referenciar al ViewModel o a las propiedades que componen la regla de validación, por ejemplo incluir texto introducido como parte del mensaje de validación.
- La salida del texto de validación puede ajustarse usando formateadores personalizados, no solo permitiendo salida en una o varias líneas, sino también en plataformas como Android donde es posible lograr renderizados más ricos, p. ej. negrita/cursiva.
Contribuir
ReactiveUI.Validation se desarrolla bajo una licencia de código abierto aprobada por la OSI, lo que la hace de uso y distribución libre, incluso para uso comercial. Valoramos a las personas involucradas en este proyecto, y nos encantaría tenerte a bordo, especialmente si recién estás comenzando o nunca antes has contribuido a un proyecto de código abierto.
Así que, para ti, persona estupenda que quiere unirse a nosotros, así es como puedes apoyarnos:
- Respondiendo preguntas en GitHub Discussions
- Transmitiendo conocimiento y formando a la próxima generación de desarrolladores
- Enviando actualizaciones de documentación donde lo consideres apropiado o necesario.
- Realizando contribuciones al código base.
Derechos de autor y licencia
Código publicado bajo la licencia MIT.