2009-06-25 6 views
2

Nous savons tous à quel point la validation de WPF est prête à l'emploi. J'essaie une chose très simple et pour une raison quelconque, il échoue toujours. J'ai un TextBox et ma seule exigence est de valider que l'utilisateur entre quelque chose dans le TextBox. Le TextBox est lié à un objet Client avec les propriétés Prénom et Nom.Les règles de validation de liaison TextBox de WPF ne se déclenchent pas sur LostFocus lorsque TextBox est vide

Voici le code XAML:

  <TextBox Style="{StaticResource TextBoxStyle}" Grid.Column="1" Grid.Row="0" Height="20" Width="100" Margin="10"> 
       <TextBox.Text> 
        <Binding Path="FirstName" > 
         <Binding.ValidationRules> 
          <ExceptionValidationRule /> 
         </Binding.ValidationRules> 
        </Binding> 
       </TextBox.Text> 

      </TextBox> 

Voici la propriété classe Client Prénom:

public string FirstName 
     { 
      get { return _firstName;} 
      set 
      { 
       if(String.IsNullOrEmpty(value)) 
        throw new ApplicationException("FirstName cannot be null or empty!"); 
       _firstName = value; 

       OnPropertyChanged("FirstName"); 
      } 
     } 

Même si je lance une exception si la FirstName (valeur) est nulle ou videz est seulement manipulé si je tape quelque chose dans le TextBox et puis le supprime et tabule ensuite. La raison en est que cela dépend de l'événement changé de propriété. Mais même si je mets cette liaison TextBox sur Focus, elle ne déclenche pas la validation.

MISE À JOUR:

Une des façons les plus laides de traiter cette question est d'attribuer le String.Empty aux champs de saisie sur l'événement Window.Loaded:

void AddCustomerWindow_Loaded(object sender, RoutedEventArgs e) 
     { 
      // get all the textboxes and set the property to empty strings! 

      txtFirstName.Text = String.Empty; 
      txtLastName.Text = String.Empty; 
     } 

Voici le code pour la liaison:

public AddCustomerWindow() 
     { 
      InitializeComponent(); 

      this.Loaded += new RoutedEventHandler(AddCustomerWindow_Loaded); 

      gvAddCustomer.DataContext = new Customer(); 
     } 

Répondre

0

Dans votre exemple code XAML, je ne vois aucune liaison. Je suppose que la propriété Text de TextBox est liée à FirstName?

Deuxièmement: FirstName est-il initialisé au début ou renvoie-t-il null lorsqu'il est récupéré par le TextBox? La liaison à une valeur null donne toujours des comportements étranges ...

Essayez de dire cela dans la propriété:

public string FirstName 
    { 
     get 
     { 
      if (_firstName == null) 
      { 
       _firstName = String.Empty; 
      } 
      return _firstName; 
     } 
     set 
     { 
      if (String.IsNullOrEmpty(value)) 
       throw new ApplicationException("FirstName cannot be null or empty!"); 
      _firstName = value; 

      OnPropertyChanged("FirstName"); 
     } 
    } 
+0

Comme, vous pouvez voir le code mis à jour la zone de texte est lié à un nouvel objet client. Lorsque je ne tape rien dans le TextBox et l'onglet à un contrôle différent rien ne se passe. Il devrait déclencher la validation mais ce n'est pas le cas! La seule façon de déclencher la validation est d'affecter manuellement la propriété .Text de TextBox dans l'événement Window.Loaded. – azamsharp

+0

Oui, je vois. Et quand le Prénom est appelé, renvoie-t-il une chaîne ou une valeur nulle? – user112889

+0

Il retourne null! – azamsharp

0

Je pense que vous avez juste besoin de spécifier le UpdateSourceTrigger et vous devriez être bon d'aller (note: I généralement ajouter les ValidatesOnDataErrors = true):

0

au lieu d'avoir à mettre toutes les valeurs au début, ou utilisez l'instruction if vous pouvez définir une variable privée au départ:

private string _FirstName = String.Empty; 
public string FirstName 
{ 
    get { return _FirstName; } 
    set 
    { 
     // Probably check for null and handle it here first 
     _FirstName = value; 
     OnPropertyChanged("FirstName"); 
    } 
} 
0

La solution que j'utilise consiste à câbler les contrôles pour mettre à jour leurs propres sources de liaison chaque fois qu'ils perdent le focus. Cela rend les contrôles valides après que l'utilisateur a tabulé ou cliqué dessus une fois.

public AddCustomerWindow() 
{ 
    InitializeComponent(); 
    this.Loaded += AddCustomerWindow_Loaded; 
    gvAddCustomer.DataContext = new Customer(); 
} 

void AddCustomerWindow_Loaded(object sender, RoutedEventArgs e) 
{ 
    // Manually wire up textboxes of specific interest. 
    UpdateSourceOnLostFocus(new TextBox[] {txtFirstName, txtLastName}, 
          TextBox.TextProperty); 

    // Or, wire up ALL controls of a given type with the help of Telerik's 
    // ChildrenOfType extension, using Linq to filter the list where appropriate. 
    UpdateSourceOnLostFocus(this.ChildrenOfType<RadDatePicker>() 
           .Where(picker => !picker.IsReadOnly), 
          RadDatePicker.SelectedValueProperty); 
} 

void UpdateSourceOnLostFocus<T>(IEnumerable<T> controls, DependencyProperty prop) where T : Control 
{ 
    controls.ToList() 
     .ForEach(ctrl => 
     { 
      var binding = BindingOperations.GetBindingExpression(ctrl, prop); 
      if (binding != null) 
       ctrl.LostFocus += (sender, args) => binding.UpdateSource(); 
     }); 
} 

Si vous souhaitez également les commandes pour valider la charge - c.-à- avant les onglets utilisateur/clique sur eux - alors vous pouvez écrire une méthode UpdateSourceNow similaire à celui ci-dessus, et l'appeler de AddCustomerWindow_Loaded au lieu .

Cependant, je préfère attendre que chaque contrôle soit visité, car il peut être quelque peu alarmant pour les utilisateurs s'ils voient immédiatement un écran plein de cases rouges lorsqu'ils veulent entrer une nouvelle instance d'objet Modèle.

Questions connexes