La validation WPF ne se déclenche pas sur le premier LostFocus de la zone de texte

StackOverflow https://stackoverflow.com/questions/103575

  •  01-07-2019
  •  | 
  •  

Question

J'essaie de valider le formulaire WPF par rapport à un objet. La validation se déclenche lorsque je tape quelque chose dans la zone de texte, perd le focus, revient dans la zone de texte, puis efface tout ce que j'ai écrit. Mais si je charge simplement l'application WPF et que je désactive la zone de texte sans écrire ni effacer quoi que ce soit de la zone de texte, elle n'est pas déclenchée.

Voici la classe Customer.cs:

public class Customer : IDataErrorInfo
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public string Error
        {
            get { throw new NotImplementedException(); }
        }
        public string this[string columnName]
        {
            get
            {
                string result = null;

                if (columnName.Equals("FirstName"))
                {
                    if (String.IsNullOrEmpty(FirstName))
                    {
                        result = "FirstName cannot be null or empty"; 
                    }
                }
                else if (columnName.Equals("LastName"))
                {
                    if (String.IsNullOrEmpty(LastName))
                    {
                        result = "LastName cannot be null or empty"; 
                    }
                }
                return result;
            }
        }
    }

Et voici le code WPF:

<TextBlock Grid.Row="1" Margin="10" Grid.Column="0">LastName</TextBlock>
<TextBox Style="{StaticResource textBoxStyle}" Name="txtLastName" Margin="10"
         VerticalAlignment="Top" Grid.Row="1" Grid.Column="1">
    <Binding Source="{StaticResource CustomerKey}" Path="LastName"
             ValidatesOnExceptions="True" ValidatesOnDataErrors="True"
             UpdateSourceTrigger="LostFocus"/>         
</TextBox>
Était-ce utile?

La solution

Si vous ne craignez pas de mettre un peu de logique dans votre code, vous pouvez gérer l'événement LostFocus avec quelque chose comme ceci:

.xaml

<TextBox LostFocus="TextBox_LostFocus" ....

.xaml.cs

private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
     ((Control)sender).GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

Autres conseils

Malheureusement, c’est intentionnel. La validation WPF ne se déclenche que si la valeur du contrôle a été modifiée.

Incroyable, mais vrai. Jusqu'ici, la validation WPF est la grande douleur proverbiale - c'est terrible.

Cependant, vous pouvez extraire l'expression de liaison de la propriété du contrôle et appeler manuellement les validations. C'est nul, mais ça fonctionne.

J'ai découvert le meilleur moyen pour moi de gérer cela, c'était lors de l'événement LostFocus de la zone de texte Je fais quelque chose comme ça

    private void dbaseNameTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        if (string.IsNullOrWhiteSpace(dbaseNameTextBox.Text))
        {
            dbaseNameTextBox.Text = string.Empty;
        }
    }

Ensuite, il voit une erreur

J'ai rencontré le même problème et trouvé un moyen très simple de résoudre ce problème: dans l'événement Loaded de votre fenêtre, mettez simplement txtLastName.Text = String.Empty. C'est tout!! Comme la propriété de votre objet a changé (une chaîne vide a été définie), la validation est activée!

Le code suivant parcourt tous les contrôles et les valide. Pas nécessairement la manière préférée mais semble fonctionner. Il ne fait que TextBlocks et TextBoxes mais vous pouvez le changer facilement.

public static class PreValidation
{

    public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }


    public static void Validate(DependencyObject depObj)
    {
        foreach(var c in FindVisualChildren<FrameworkElement>(depObj))
        {
            DependencyProperty p = null;

            if (c is TextBlock)
                p = TextBlock.TextProperty;
            else if (c is TextBox)
                p = TextBox.TextProperty;

            if (p != null && c.GetBindingExpression(p) != null) c.GetBindingExpression(p).UpdateSource();
        }

    }
}

Appelez simplement Validate sur votre fenêtre ou sur votre contrôle, il devrait les pré-valider pour vous.

scroll top