mvvm c#
MVVM-ValidaciĆ³n (2)
¿Hay un patrón común para resolver ese problema? no queremos introducir una dependencia en la GUI entre los dos cuadros de texto, porque esta lógica solo debería estar presente en la capa de lógica empresarial.
Value1
yValue2
son interdependientes debido a la condición "Value1 and Value2 en el modelo no debe ser el mismo".Esto significa que cuando
Value2
cambia,Value1
también cambia, ¡y viceversa! En realidad, cuandoValue2
cambia, el resultado de la validación deValue1
cambia, pero esto está cerca de la declaración anterior.Value1
yValue2
deben notificar sobre el cambio de propiedad deValue1
yValue2
.La vista debe releer y revalidar ambos valores y borrar la marca defectuosa.
No estoy seguro de si
WPF
lo hará si encuentra que el evento de notificación se ha generado pero el valor realmente no ha cambiado.
Estamos tratando de averiguar la validación en el mvvm haciendo la validación en la lógica de negocios o modelo. He implementado el tipo de validación por excepción en nuestra lógica de negocios; aquí se puede encontrar un diagrama simplificado:
Si tenemos muchas entradas que son independientes entre sí, no hay problema, se lanza la excepción, el cuadro de texto lo atrapa y marca los bordes en rojo por cada entrada incorrecta. Sin embargo, cuando tenemos valores dependientes estamos en problemas. p.ej
Value1 y Value2 en el modelo no deben ser iguales, por lo que tenemos una función de validación en cada uno de los que buscan el valor igual y lanzamos una excepción si eso sucede.
Ahora, si establecemos Value1 en 0 y Value2 en 1, todo está bien
Value1 se establece en la GUI en 1 -> este se marca en rojo, porque la validación de los otros valores no se activa, por lo que Value2 en la GUI no está marcado como defectuoso
Value2 se establece en 2 en la GUI, ahora hemos alcanzado un estado válido, pero solo se valida Value2, por lo que Value1 aún está marcado como defectuoso
¿Hay un patrón común para resolver ese problema? no queremos introducir una dependencia en la GUI entre los dos cuadros de texto, porque esta lógica solo debería estar presente en la capa de lógica empresarial.
En lugar de implementar la validación por excepción, también se podría implementar la interfaz IDataErrorInfo, pero el problema persiste, no hay forma de forzar valores dependientes para validar sus valores nuevamente, al menos ninguno que pueda ver :)
Cualquier ayuda es apreciada
saludos, manni
[limpieza, eliminado paso innecesario]
15.11.2010 - Parte 2
ok, repensamos mucho aquí, vamos con el nivel de lógica de negocios. Aquí está nuestra configuración actual planeada: (la imagen es un poco pequeña aquí, ábrala en una ventana separada para mostrarla en tamaño completo) todo es más o menos claro, excepto cómo notificar a todos los modelos de viewmodels / model de los diferentes editores si el modelo de datos bajo la lógica del negocio se cambia. Una forma de hacerlo es rastrear los modelos clonados en la lógica de negocios que los crea. Cuando se cambia el modelo de datos utilizando la lógica de negocios commit (), todos los demás clones de modelos registrados pueden ser notificados de los cambios y propagarlos más. como alternativa, la lógica de negocios podría publicar un evento al que todos los modelos de vista se suscriban para que también obtengan los cambios. ¿Alguien podría darme una pista de algo mejor?
Gracias de nuevo por la ayuda, lo siento, estoy tan bloqueado mentalmente;)
Podría considerar el uso de la interfaz System.ComponentModel.IDataErrorInfo . Esta interfaz muy práctica te da la capacidad de:
- hacer la validación de una manera compatible con MVVM
- hacer validación personalizada para cualquier campo en particular (la validación podría verificar varios valores si lo desea)
- vincular su interfaz de usuario a los errores de validación
Usted implementa IDataErrorInfo en su modelo de vista (o incluso virtualmente en su base de modelo de vista, y lo reemplaza en sus modelos de vista derivados). Debido a la naturaleza del enlace de datos, los valores que necesito verificar están todos en el modelo de vista, y puedo probar cualquier combinación de ellos. Por supuesto, todavía tiene su validación en su capa de negocios, pero ya no necesita hacer un viaje a su capa de negocios (o Modelo) solo para realizar una validación.
Aquí hay un ejemplo rápido de una pantalla (WPF) que recopila algunos detalles del usuario y realiza una validación básica en ellos:
Código C #:
#region IDataErrorInfo Members
/// <summary>
/// Gets an error message indicating what is wrong with this object.
/// </summary>
/// <value></value>
/// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
public override string Error
{
get
{
return this["UserCode"] + this["UserName"] + this["Password"] + this["ConfirmedPassword"] + this["EmailAddress"];
}
}
/// <summary>
/// Gets the <see cref="System.String"/> with the specified column name.
/// </summary>
/// <value></value>
public override string this[string columnName]
{
get
{
switch (columnName)
{
case "UserCode":
if (!string.IsNullOrEmpty(UserCode) && UserCode.Length > 20)
return "User Code must be less than or equal to 20 characters";
break;
case "UserName":
if (!string.IsNullOrEmpty(UserCode) && UserCode.Length > 60)
return "User Name must be less than or equal to 60 characters";
break;
case "Password":
if (!string.IsNullOrEmpty(Password) && Password.Length > 60)
return "Password must be less than or equal to 60 characters";
break;
case "ConfirmedPassword":
if (Password != ConfirmedPassword)
return Properties.Resources.ErrorMessage_Password_ConfirmedPasswordDoesntMatch;
break;
case "EmailAddress":
if (!string.IsNullOrEmpty(EmailAddress))
{
var r = new Regex(_emailRegex);
if (!r.IsMatch(EmailAddress))
return Properties.Resources.ErrorMessage_Email_InvalidEmailFormat;
}
break;
}
return string.Empty;
}
}
#endregion
y aquí está el marcado XAML para dos de los cuadros de texto en la página (tenga en cuenta particularmente las propiedades ValidatesOnDataErrors
y ValidatesOnExceptions
en el enlace de Text
):
<TextBox Name="UserCodeTextBox"
Text="{Binding UserCode,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
ValidatesOnExceptions=True,
NotifyOnSourceUpdated=True,
NotifyOnTargetUpdated=True}"
GotFocus="Input_GotFocus"
VerticalAlignment="Top"
Margin="165,0,150,0"
CharacterCasing="Upper"
/>
<TextBox Name="UserNameTextBox"
Text="{Binding UserName,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
ValidatesOnExceptions=True,
NotifyOnSourceUpdated=True,
NotifyOnTargetUpdated=True}"
GotFocus="Input_GotFocus"
VerticalAlignment="Top"
Margin="165,30,0,0"
/>