Merci à Michael pour son aide. Malheureusement, j'ai besoin de liaisons pour rester constamment synchronisé, avec seulement la validation différée. Sinon, la solution de Michael ferait l'affaire. Donc, je suis allé dans une direction légèrement différente. Voici la solution que j'ai finalement mise en place.
Démonstration simple: Commençons par le cas le plus simple: Mes objets métier implémentent IDataErrorInfo
; Supposons que je puisse modifier la façon dont il est mis en œuvre. Je donne à chaque objet métier une propriété booléenne supplémentaire, ValidationEnabled
, et je modifie l'implémentation IDataErrorInfo
pour toujours retourner un résultat nul si cette propriété est fausse.
La démo dont j'ai parlé dans mon article original utilise un objet Contact avec deux propriétés; Prénom et nom. J'ai ajouté une propriété ValidationEnabled et modifié la mise en œuvre IDataErrorInfo
ressemble à ceci:
#region IDataErrorInfo Members
public string Error
{
get { throw new System.NotImplementedException(); }
}
public string this[string columnName]
{
get
{
// Initialize
string result = null;
// Perform validation only if enabled
if (ValidationEnabled)
{
switch (columnName)
{
// Validate 'First Name'
case "FirstName":
if (string.IsNullOrEmpty(FirstName))
{
result = "First name has to be set";
}
else if(FirstName.Length < 5)
{
result = "First name must be at least five characters";
}
break;
// Validate "Last Name"
case "LastName":
if (string.IsNullOrEmpty(LastName))
{
result = "Last name has to be set";
}
else if (LastName.Length < 5)
{
result = "Last name must be at least five characters";
}
break;
}
}
// Set return value
return result;
}
}
#endregion
Dans ma version de la démo simple, je me connecter sur le bouton Envoyer à un gestionnaire d'événements dans le code-behind qui permet la validation et actualise les fixations pour les deux zones de texte:
private void OnButtonClick(object sender, RoutedEventArgs e)
{
var contact = (Contact) DataContext;
contact.ValidationEnabled = true;
var binding = FirstNameBox.GetBindingExpression(TextBox.TextProperty);
binding.UpdateSource();
binding = LastNameBox.GetBindingExpression(TextBox.TextProperty);
binding.UpdateSource();
}
Maintenant, les contours rouges n'apparaissent que lorsque j'essaie de soumettre le formulaire.
Monde réel: Dans mon application de production, je ne contrôle pas les objets métier, et je ne peux pas les modifier comme je l'ai fait dans ma démo simple. Ainsi, je crée un encapsuleur simple pass-through pour l'objet métier qui expose les propriétés à databound à la vue et relie ces propriétés aux propriétés correspondantes de l'objet métier encapsulé. L'encapsuleur implémente IDataErrorInfo et contient la propriété ValidationEnabled
.
L'implémentation IDataErrorInfo du wrapper renvoie toujours null si la validation n'est pas activée. Si la validation est activée, l'encapsuleur appelle l'implémentation IDataErrorInfo sur l'objet encapsulé et renvoie ce qu'il obtient à partir de là.
Cette approche sera familière à quiconque utilisera le modèle Model-View-ViewModel. Ce que nous faisons est de créer un wrapper de modèle de vue pour un objet métier, ce qui est considéré comme une meilleure pratique MVVM par de nombreux développeurs. Il sépare le problème de l'interface utilisateur (suppression des bordures rouges jusqu'à ce que la page soit soumise) du problème du modèle métier (validation d'objet simple).
De plus, mon application de production n'utilisera pas les gestionnaires d'événements dans le code-behind. Par MVVM, le bouton sera câblé à un ICommand, qui contiendra la logique du gestionnaire OnButtonClick() dans la démo simple. J'espère que cela sera utile à quelqu'un d'autre à la recherche de ce problème sur la route.
C'est une solution partielle - Mes bases de données ne sont pas mises à jour tant que je ne les ai pas soumises. Cependant, j'apprécie l'aide, donc c'est accepté par moi. J'ai détaillé ci-dessous une solution plus compliquée qui permet de synchroniser les liaisons mais qui reporte la validation jusqu'à la soumission de la page. –