Laden...
FAQ

[FAQ] Event nur bei Benutzeraktion auslösen, nicht bei programmtechnischer Änderung

Erstellt von herbivore vor 14 Jahren Letzter Beitrag vor 14 Jahren 25.772 Views
herbivore Themenstarter:in
49.485 Beiträge seit 2005
vor 14 Jahren
[FAQ] Event nur bei Benutzeraktion auslösen, nicht bei programmtechnischer Änderung

Hallo Community!

Das Problem

Wie kann man erreichen, dass ein Event (z.B. TextBox.TextChanged) nur ausgelöst wird, wenn der Benutzer die Aktion durchführt, nicht aber wenn das Programm die Aktion durchführt.

Die Lösung(en)

Meisten geht es gar nicht darum, dass das Event nicht ausgelöst wird, sondern das eigentliche Ziel ist, dass der eigene Ereignisbehandlungscode - oder Teile davon - nicht ausgeführt werden soll. Dafür gibt es zwei einfache Möglichkeiten.

Boolesche Variable setzen

Die einfachte Lösung ist die Verwendung einer boolschen Variable (z.B. _ignoreEvents, _ignoreTextChanged, _ignoreTextBox1TextChanged, ...) die vor der programmtechnischen Änderung gesetzt und danach zurückgesetzt wird. Am Anfang des EventHandlers fragt man die Variable ab und verlässt ihn, wenn die Variable gesetzt ist. Damit das Zurücksetzen auch im Fehlerfall sauber funktioniert, sollte man es im finally durchführen. Das ist eine einfache, kurze, flexible und elegante Lösung.


private bool _ignoreEvents = false;

private void DoSomething ()
{
   _ignoreEvents = true;
   try {
      textBox1.Text = "Hallo";
   }
   finally {
      _ignoreEvents = false;
   }
}

protected void textBox1_TextChanged (Object objSender, EventArgs e)
{
   if (_ignoreEvents) { return; }
   // hier die eigentliche Aktion durchführen
}

Natürlich muss man den EventHandler nicht komplett verlassen, sondern kann in Abhängigkeit von der boolschen Variable auch nur Teile ignorieren oder Teile gerade nur dann ausführen, wenn die Änderung programmtechnisch erfolgt (else-Zweig). Man hat also die volle Flexibilität.

Eventhandler deregistrieren

Eine Alternative, die genauso kurz und einfach, jedoch weniger elegant und flexibel ist, wäre den EventHandler temporär zu deregistrieren.


private void DoSomething ()
{
   textBox1.TextChanged -= textBox1_TextChanged;
   try {
      textBox1.Text = "Hallo";
   }
   finally {
      textBox1.TextChanged += textBox1_TextChanged;
   }
}

protected void textBox1_TextChanged (Object objSender, EventArgs e)
{
   // hier die eigentliche Aktion durchführen
}

Man spart sich dabei zwar die boolsche Variable, dafür kann man aber den EventHander nur ganz oder gar nicht deregistrieren, ist also auch weniger flexibel.

Das Auslösen des Events verhindern

Beide bisher genannten Lösungen wirken nur auf die EventHandler, die man explizit berücksichtigt, also in denen man explizit die boolsche Variable abfragt bzw. die man explizit deregistriert. Das ist - wie gesagt - meistens genau das, was man will. Es ist allerdings auch möglich, das Aufrufen aller EventHander zu verhindern, wenn das Event nach der Empfehlung von Microsoft implementiert wurde. Dazu muss man allerdings die Klasse, die das Event auslöst, überschreiben und dann auch Objekte der Unterklasse verwenden, im folgenden Beispiel also MyTextBoxen statt normalen TextBoxen.

public class MyTextBox : TextBox
{
   private bool _disableTextChanged = false;

   public bool DisableTextChanged
   {
      get { return _disableTextChanged; }
      set { _disableTextChanged = value; }
   }

   protected override void OnTextChanged (EventArgs e)
   {
      if (_disableTextChanged) { return; }
      base.OnTextChanged (e);
   }
}

...

   private void DoSomething ()
   {
         textBox1.DisableTextChanged = true;
         try {
            textBox1.Text = "Hallo";
         }
         finally {
            textBox1.DisableTextChanged = false;
         }
   }

   protected void textBox1_TextChanged (Object objSender, EventArgs e)
   {
      // hier die eigentliche Aktion durchführen
   }

Noch eine spezielle, dennoch wichtige Anmerkung zum Schluss: Bei dieser letzten Lösung muss man berücksichtigen, das hierdurch nur die Event_Handler_ deaktiviert werden. Die eventauslösende OnTextChanged-Methode wird weiterhin aufgerufen. Für eine Unterklasse von MyTextBox, die OnTextChanged überschreibt statt TextChanged zu abonnieren, sieht es also so aus, als würde das Event immer ausgelöst. Sie müsste also im Zweifel selbst explizit auf DisableTextChanged abfragen. Die Situation könnte man verhindern, indem man die Methode MyTextBox.OnTextChanged sealed macht, was dann aber wiederum die Möglichkeiten der Unterklasse (zu sehr) einschränkt. Im Ergebnis sollte man also lieber eine der erstgenannten Lösungen bevorzugen.

Siehe auch

[FAQ] Eigenen Event definieren / Information zu Events
[FAQ] Bestimmte Aktionen bis nach der laufenden GUI-Event-Behandlung verzögern

herbivore