Evénement TextBox.TextChanged déclenché deux fois sur un émulateur Windows Phone 7

J’ai une application de test très simple pour jouer avec Windows Phone 7. Je viens d’append une TextBox et un TextBlock au modèle standard de l’interface utilisateur. Le seul code personnalisé est le suivant:

 public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); } private int counter = 0; private void TextBoxChanged(object sender, TextChangedEventArgs e) { textBlock1.Text += "Text changed " + (counter++) + "\r\n"; } } 

L’événement TextBox.TextChanged est connecté à TextBoxChanged dans XAML:

  

Cependant, chaque fois que j’appuie sur une touche lors de l’exécution dans l’émulateur (clavier virtuel ou physique, après avoir appuyé sur Pause pour activer ce dernier), le compteur est incrémenté deux fois, affichant deux lignes dans le TextBlock . Tout ce que j’ai essayé montre que l’événement tire véritablement deux fois et je ne sais pas pourquoi. J’ai vérifié qu’il n’est abonné qu’une seule fois – si je me désabonne du constructeur MainPage , rien ne se passe (dans le bloc de texte) lorsque le texte change.

J’ai essayé le code équivalent dans une application Silverlight classique, et cela ne s’est pas produit là. Je n’ai pas de téléphone physique pour le reproduire pour le moment. Je n’ai trouvé aucune trace de problème connu dans Windows Phone 7.

Quelqu’un peut-il expliquer ce que je fais mal, ou devrais-je signaler cela comme un bug?

EDIT: Pour réduire la possibilité que cela soit dû à deux contrôles de texte, j’ai essayé de supprimer complètement TextBlock et de changer la méthode TextBoxChanged pour simplement incrémenter le counter . J’ai ensuite couru dans l’émulateur, tapé 10 lettres puis mis un point d’arrêt sur le counter++; line (juste pour se débarrasser de toute possibilité que la rupture dans le débogueur provoque des problèmes) – et il affiche counter comme 20.

EDIT: J’ai maintenant demandé dans le forum Windows Phone 7 … nous verrons ce qui se passe.

La raison pour laquelle l’événement TextChanged déclenché deux fois dans WP7 est un effet secondaire de la manière dont la zone de TextBox a été conçue pour l’apparence Metro.

Si vous modifiez le modèle TextBox dans Blend, vous verrez qu’il contient une zone de TextBox secondaire pour l’état désactivé / en lecture seule. Cela provoque, comme effet secondaire, le déclenchement de l’événement deux fois.

Vous pouvez modifier le modèle pour supprimer le TextBox supplémentaire (et les états associés) si vous n’avez pas besoin de ces états, ou modifier le modèle pour obtenir un aspect différent dans l’état désactivé / lecture seule, sans utiliser un TextBox secondaire.

Avec cela, l’événement ne se déclenchera qu’une fois.

j’irais pour le bogue, principalement parce que si vous y mettez les événements KeyDown et KeyUp , cela montre qu’ils ne sont déclenchés qu’une seule fois (chacun d’eux) mais que l’événement TextBoxChanged est déclenché deux fois

Cela ressemble à un bug pour moi. Pour contourner ce problème, vous pouvez toujours utiliser DistinctUntilChanged de Rx. Il y a une surcharge qui vous permet de spécifier la clé distincte.

Cette méthode d’extension renvoie l’événement TextChanged observable mais ignore les doublons consécutifs:

 public static IObservable> GetTextChanged( this TextBox tb) { return Observable.FromEvent( h => textBox1.TextChanged += h, h => textBox1.TextChanged -= h ) .DistinctUntilChanged(t => t.Text); } 

Une fois le bogue corrigé, vous pouvez simplement supprimer la ligne DistinctUntilChanged .

Agréable! J’ai trouvé cette question en recherchant un problème connexe et j’ai également trouvé cette chose ennuyante dans mon code. Le double événement consum plus de ressources CPU dans mon cas. Donc, j’ai corrigé mon filtre en temps réel avec cette solution:

 private ssortingng filterText = Ssortingng.Empty; private void SearchBoxUpdated( object sender, TextChangedEventArgs e ) { if ( filterText != filterTextBox.Text ) { // one call per change filterText = filterTextBox.Text; ... } } 

Je crois que cela a toujours été un bogue dans le Compact Framework. Il doit avoir été transféré dans WP7.

Bien sûr, cela ressemble à un bogue, si vous essayez de déclencher un événement chaque fois que le texte change, vous pouvez essayer d’utiliser une liaison bidirectionnelle. Malheureusement, cela ne déclenche pas d’événements de changement de presse par clé perd le focus). Voici une solution si vous en avez besoin:

  this.textBox1.TextChanged -= this.TextBoxChanged; textBlock1.Text += "Text changed " + (counter++) + "\r\n"; this.textBox1.TextChanged += this.TextBoxChanged; 

Disclaimer – Je ne suis pas familier avec les nuances de xaml et je sais que cela semble illogique … mais de toute façon, ma première pensée est d’essayer de passer comme un simple eventarg plutôt que du textchangedeventargs. Cela n’a pas de sens, mais peut-être que cela pourrait aider? Il semble que lorsque j’ai vu des doublages comme celui-ci avant, c’est soit à cause d’un bug, soit parce que j’ajoute des appels au gestionnaire d’événements en coulisse … Je ne sais pas trop si?

Si vous avez besoin de rapidité et de saleté, encore une fois, je ne suis pas habitué à xaml – la prochaine étape serait de sauter xaml pour cette zone de texte comme solution rapide … faites cette zone de texte totalement dans c # jusqu’à ce que vous puissiez identifier le bogue ou code difficile … si vous avez besoin d’une solution temporaire.

Je ne pense pas que ce soit un bogue. Lorsque vous atsortingbuez la valeur à une propriété de texte à l’intérieur de l’événement textchanged, la valeur de la zone de texte est modifiée pour appeler à nouveau l’événement de modification du texte.

Essayez ceci dans l’application Windows Forms, vous pourriez avoir une erreur

“Une exception non gérée de type ‘System.StackOverflowException’ s’est produite dans System.Windows.Forms.dll”

StefanWick a raison, pensez à utiliser ce modèle

       

C’est un vieux sujet, mais au lieu de changer de modèle (cela ne fonctionne pas pour moi, je ne vois pas l’autre zone de texte avec Blend), vous pouvez append un booléen pour vérifier si l’événement a déjà fait la fonction ou non.

 boolean already = false; private void Tweet_SizeChanged(object sender, EventArgs e) { if (!already) { already = true; ... } else { already = false; } } 

Je suis conscient que ce n’est PAS le moyen parfait, mais je pense que c’est la façon simple de le faire. Et il fonctionne.