2010-06-16 4 views
6

J'ai une zone de texte dont la valeur est binded à une propriété ViewModel:WPF valeur TextBox ne change pas OnPropertyChanged

 <TextBox Name="txtRunAfter" Grid.Column="4" Text="{Binding Mode=TwoWay, Path=RunAfter}" Style="{StaticResource TestStepTextBox}"/> 

L'ensemble et se travaillaient bien jusqu'à ce que j'ai essayé d'ajouter une validation lorsque la valeur est set:

private int _runAfter = 0; 
    public string RunAfter 
    { 
     get 
     { 
      return _runAfter.ToString(); 
     } 

     set 
     { 
      int val = int.Parse(value); 

      if (_runAfter != val) 
      { 
       if (val < _order) 
        _runAfter = val; 
       else 
       { 
        _runAfter = 0; 
        OnPropertyChanged("RunAfter"); 
       } 
      } 
     } 
    } 

Bien que le OnPropertyChanged soit atteint (je dubugged que), la vue ne change pas. Comment puis-je faire ce travail?

Merci, José Tavares

Répondre

5

Le problème est que vous mettez à jour la source pour le Binding tandis que le Binding met à jour votre propriété. WPF ne vérifie pas réellement la valeur de votre propriété lorsqu'il déclenche l'événement PropertyChanged en réponse à une mise à jour Binding. Vous pouvez résoudre ce problème en utilisant le Dispatcher pour retarder la propagation de l'événement dans cette branche:

set 
{ 
    int val = int.Parse(value); 

    if (_runAfter != val) 
    { 
     if (val < _order) 
     { 
      _runAfter = val; 
      OnPropertyChanged("RunAfter"); 
     } 
     else 
     { 
      _runAfter = 0; 
      Dispatcher.CurrentDispatcher.BeginInvoke(
       new Action<String>(OnPropertyChanged), 
       DispatcherPriority.DataBind, "RunAfter"); 
     } 
    } 
} 

Mise à jour:

L'autre chose que je remarque est que le Binding sur votre TextBox est d'utiliser la valeur par défaut UpdateSourceTrigger, ce qui se produit lorsque le TextBox perd le focus. Vous ne verrez pas le texte revenir à 0 jusqu'à ce que le TextBox perd son focus avec ce mode. Si vous le changez en PropertyChanged, vous verrez cela se produire immédiatement. Dans le cas contraire, votre propriété ne sera pas activé jusqu'à ce que votre TextBox désélectionné:

<TextBox Name="txtRunAfter" Grid.Column="4" Text="{Binding RunAfter, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource TestStepTextBox}"/> 
+0

Je suppose que votre évaluation du problème est correcte, mais l'appel dispatcher ne fonctionne pas. Mon UserControl est utilisé dans une application WinForm en utilisant un ElementHost. Cela peut-il affecter l'appel Dispatcher? – jpsstavares

+0

D'accord, j'ai testé cela et ça a bien fonctionné (quand vous tabulez loin de la TextBox car le mode par défaut de la liaison ne mettra pas à jour la propriété tant que la TextBox ne perdra pas le focus). J'ai mis à jour la réponse pour expliquer le UpdateSourceTrigger sur les liaisons, dans le cas où c'est le comportement que vous voyez. Je ne traite pas beaucoup de WPF hébergé dans WinForms, mais je ne vois pas pourquoi cela affecterait Binding ou Dispatcher dans votre contexte. –

3

Quelques choses que je remarquai ici.

À moins que vous n'ayez une raison impérieuse d'exposer la propriété RunAfter en tant que chaîne, il n'y a aucune raison pour laquelle elle ne peut pas être un int. Cela vous sauverait la distribution dans le setter (ainsi qu'une InvalidCastException possible cachée si l'utilisateur entre quelque chose de non-entier dans le champ).

En second lieu, le OnPropertyChanged() appel devrait avoir lieu à l'extérieur de l'intérieur si la déclaration, comme ce qui suit:

if(_runAfter != val) 
{ 
    if(val < _order) 
     _runAfter = val; 
    else 
     _runAfter = 0; 
    OnPropertyChanged("RunAfter"); 
} 

Depuis le local est mis à jour _runAfter dans les deux voies du conditionnel, l'OnPropertyChanged() a être appelé indépendamment de la branche prise. J'espère que cela vous aidera à vous orienter dans la bonne direction!

+0

+1 pour noter que vous devez déplacer le OnPropertyChanged extérieur de l'autre. – Robaticus

+0

Eh bien, l'ensemble est appelé par la vue (via la liaison), donc je pensais qu'il serait seulement nécessaire d'appeler OnPropertyChanged si la valeur serait modifiée par rapport à celle définie dans la vue. – jpsstavares

+0

Quoi qu'il en soit, cela n'a pas résolu mon problème ... – jpsstavares

1

J'ai eu la même situation. J'ai écrit ce qui suit et ça a marché.

<TextBox Grid.Column="3" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" Text="{Binding Path=FirstName}"></TextBox>

Et

public string FirstName 
    { 
     get { return _client.FirstName; } 
     set 
     { 
      if (value == _client.FirstName) 
       return; 
      else 
       _client.FirstName = value; 
      OnPropertyChanged("FirstName"); 
     } 
    } 
Questions connexes