2014-11-19 14 views
35

Ich habe eine einfache Anwendung, die jeden eingegebenen Text in einem anderen Textfeld umkehrt. Der Catch ist, Sie können beide Textfelder ändern und die Änderungen werden (wörtlich) in den anderen reflektiert.Warum verursacht das nicht eine unendliche Schleife von Ereignissen?

Ich habe diesen Code geschrieben, weil ich glaube, dass er Probleme verursacht.

private void realText_TextChanged(object sender, EventArgs e) 
{ 
    mirrorText.Text = mirror(realText.Text); 
} 

private void mirrorText_TextChanged(object sender, EventArgs e) 
{ 
    realText.Text = mirror(mirrorText.Text); 
} 

private string mirror(string text) 
{ 
    return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n"); 
} 

Ich versuchte es dann aus, zu glauben, dass es eine unendliche Schleife verursachen würde (realText ändert mirrorText, geschieht ein weiteres Ereignis, mirrorTextrealText ändert, etc). Außer dem beabsichtigten Verhalten ist jedoch nichts passiert.

Ich bin natürlich glücklich darüber, ich könnte es hier lassen. Oder könnte ich?

Ich bin ziemlich sicher, dass das Ereignis TextChanged ausgelöst werden soll, wenn geändert wird. Ist das beabsichtigte Verhalten eines Fehlerschutzes bei den Ereignissen, oder hatte ich nur Glück? Kann sich dieser Code auf einem anderen Computer mit anderen Buildeinstellungen usw. als falsch verhalten?

private void realText_TextChanged(object sender, EventArgs e) 
{ 
    if (realText.Focused) 
    { 
     mirrorText.Text = Mirror(realText.Text); 
    } 
} 

Ich werde wahrscheinlich tun es trotzdem sicher zu sein, aber ist es erforderlich überprüfen dies: Es kann leicht behoben? (Ich werde nicht einmal fragen, ob es empfohlen wird.)

+3

Ich bin ziemlich sicher TextChanged Ereignis wird nur ausgelöst, wenn der Text von der Benutzeroberfläche nicht von Code geändert wird. – Magnus

+5

@Magnus von MSDN: Dieses Ereignis wird ausgelöst, wenn die Text-Eigenschaft entweder durch eine programmatische Änderung oder Benutzerinteraktion geändert wird. – Marton

+0

@Magnus, du könntest Reflector benutzen (ich glaube, dass es hieß, richtig? Oder die JetBrains eins ... uh ... kann mich nicht an den Namen erinnern), um nachzusehen und zu sehen! –

Antwort

30

Nach den Kommentaren, und wie bereits beantwortet, wird das Ereignis TextChanged nicht ausgelöst, wenn Sie die Eigenschaft auf den Wert setzen, den es bereits hat.

Es ist nicht klar, ob Sie sich darauf verlassen können. Es ist eine sinnvolle Optimierung, und ich wäre sehr überrascht, wenn zukünftige Versionen von .NET Framework es fallen lassen, aber ich kann nicht für ältere Versionen sprechen, noch für Implementierungen von Drittanbietern (Mono).

Um absolut sicher zu sein, würde ich nicht verwenden Sie die Focused überprüfen Sie in Ihre Frage. Ich würde genau tun, was der Text Setter jetzt tut.

private void realText_TextChanged(object sender, EventArgs e) 
{ 
    var newMirrorText = Mirror(realText.Text); 
    if (mirrorText.Text != newMirrorText) 
     mirrorText.Text = newMirrorText; 
} 

Dies hat den gleichen Vorteil unendliche Rekursion zu verhindern, spielt aber mehr schön mit anderen Code, den Sie in Ihrem Formular stellen können, die den Text als Ergebnis eines anderen Ereignisses ändert.

21

Der Grund, warum es keine Schleife verursacht, ist, dass es überprüft, ob die Eigenschaft tatsächlich geändert, d. H. Wenn der neue Wert nicht den alten Wert entspricht. In Ihrem Fall passiert die mirror Funktion sich umzukehren, was nach zwei Durchgängen zum selben Text führt.

+1

@ Pietu1998 (und @Basic): Danke für die Änderungen. Ich musste die Bearbeitung selbst verzögern und fand heraus, dass es bereits zwei perfekte Bearbeitungen enthielt, als ich zurückkam. –

+0

Es tut mir leid, aber ich werde [user: hvd] das Akzeptieren geben, da sie dies zuerst vorgeschlagen haben und eine gründlichere Antwort gepostet haben. Ich weiß deine Antwort zu schätzen. – Pietu1998

+1

@ Pietu1998: Keine Sorgen :) –

-1

Wenn die Textbox einen Text enthält und wir versuchen, sie mit demselben Text zu ändern, wird das TextChange-Ereignis nicht ausgelöst, da der neue Text derselbe wie der vorherige Text ist. In Ihrem Code kehrt das realText_TextChanged-Ereignis den Text um und ändert damit den mirrorText. Das mirrorText_TextChanged-Ereignis kehrt den Text um und versucht, den RealText zu ändern. Der RealText hat bereits diesen Text und löst das Ereignis realText_TextChanged nicht aus.

3

Es ist ziemlich einfach zu überprüfen.

Zuerst ersetzen Sie beide Textboxsteuerelemente mit

class T : TextBox 
    { 
     public override string Text 
     { 
      get 
      { 
       return base.Text; 
      } 
      set 
      { 
       base.Text = value; 
      } 
     } 
    } 

Zweitens, stellen Sie den Haltepunkt auf Setter. Fügen Sie diese Ausdrücke auf die Watch-Fenster:

  • Namen
  • Text
  • Wert

Drittens starten Sie die App, Kopie '123' von irgendwo und fügen Sie ihn in die erste Textbox. Hier geht es:

1. Pause:

  • Name: "mirrorText"
  • Text: ""
  • Wert: "321"

2. Pause:

  • Name: "RealText"
  • Text: "123"
  • Wert: "123"

3. ... hoppla, ist es nicht mehr bricht. Um zu erkennen, warum wir tiefer gehen mussten. Schauen Sie sich referencesource: Textbox Setter does nothing ungewöhnlich, aber TextBoxBase ist ein looks interesting:

 set { 
      if (value != base.Text) { // Gotcha! 
       base.Text = value; 
       if (IsHandleCreated) { 
        // clear the modified flag 
        SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); 
       } 
      } 
     } 

So, wie hvd bereits beantwortet, ist der Grund der Textbox nicht Textchanged erhöhen, wenn alte und neue Werte gleich sind. Ich denke nicht, dass sich das Verhalten ändern wird, zumindest für Winforms. Aber wenn Sie eine robustere Lösung wünschen, ist es hier:

private void RunOnce(ref bool flag, Action callback) 
    { 
     if (!flag) 
     { 
      try 
      { 
       flag = true; 
       callback(); 
      } 
      finally 
      { 
       flag = false; 
      } 
     } 
    } 

    private bool inMirror; 
    private void realText_TextChanged(object sender, EventArgs e) 
    { 
     RunOnce(ref inMirror,() => 
     { 
      mirrorText.Text = mirror(realText.Text); 
     }); 
    } 

    private void mirrorText_TextChanged(object sender, EventArgs e) 
    { 
     RunOnce(ref inMirror,() => 
     { 
      realText.Text = mirror(mirrorText.Text); 
     }); 
    } 

    private string mirror(string text) 
    { 
     return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n"); 
    } 

P.S. Mirror() wird auf Ersatzpaaren fehlschlagen. Here're einige Lösungen.

+0

Vielen Dank für eine gründliche Inspektion. Ich werde wahrscheinlich nur die einfache TextBox verwenden. Und nebenbei, ich habe mirror() neu geschrieben, um etwas anderes zu tun, das nur zum Testen der GUI gedacht war. – Pietu1998