2008-09-24 15 views
17

Beim Schreiben von GUIs habe ich häufig das folgende Problem kommen: Angenommen, Sie haben ein Modell und einen Controller. Der Controller verfügt über ein Widget W, das verwendet wird, um eine Eigenschaft X des Modells anzuzeigen.Breaking Event Cycles in GUIs

Da das Modell möglicherweise von außerhalb des Controllers geändert wird (möglicherweise gibt es andere Controller, die dasselbe Modell verwenden, Operationen rückgängig machen usw.), hört der Controller Änderungen am Modell ab. Der Controller überwacht auch Ereignisse auf dem Widget W und aktualisiert die Eigenschaft X entsprechend.

Nun geschieht Folgendes:

  1. der Wert in W geändert wird
  2. ein Ereignis erzeugt wird, der Handler in der Controller
  3. die Controller aufgerufen wird den neuen Wert legt für X in der Modell
  4. die m odel emittiert Ereignisse, weil es
  5. die Controller empfängt ein Änderungsereignis vom Modell
  6. der Controller wird der Wert von X und setzt sie in das Widget
  7. goto 1.
  8. geändert wurde

Es gibt mehrere mögliche Lösungen dafür:

  1. Ändern Sie den Controller, um beim Aktualisieren des Modells ein Flag zu setzen, und reagieren Sie nicht auf Ereignisse vom Modell, wenn dieses Flag gesetzt ist.
  2. Trennen Sie den Controller vorübergehend (oder das Modell sagen, keine Ereignisse für einige Zeit zu senden)
  3. Frieren keine Updates aus dem Widget

In der Vergangenheit ging ich in der Regel für die Option 1, weil es die einfachste Sache. Es hat den Nachteil, dass Sie Ihre Klassen mit Flaggen überfluten, aber die anderen Methoden haben auch ihre Nachteile.

Nur für das Protokoll, ich hatte dieses Problem mit mehreren GUI Toolkits, einschließlich GTK +, Qt und SWT, also ich denke, es ist ziemlich toolkit-agnostic.

Best Practices? Oder ist die Architektur, die ich verwende, einfach falsch?

@Shy: Das ist eine Lösung für einige Fälle, aber Sie erhalten immer noch eine Runde überflüssiger Ereignisse, wenn X von außerhalb des Controllers geändert wird (z. B. bei Verwendung des Befehlsmusters für Rückgängig/Wiederholen), weil dann der Wert hat sich geändert, W wird aktualisiert und löst ein Ereignis aus. Um eine weitere (nutzlose) Aktualisierung des Modells zu verhindern, muss das vom Widget erzeugte Ereignis verschluckt werden.
In anderen Fällen könnte das Modell komplexer sein und eine einfache Überprüfung dessen, was sich genau geändert hat, könnte nicht durchführbar sein, z. eine komplexe Baumansicht.

+0

Ich habe dieses Problem mit Listenfeldern in MFC die ganze Zeit. –

Antwort

3

Normalerweise sollten Sie auf Eingabeereignisse im Widget reagieren und keine Ereignisse ändern. Dies verhindert, dass diese Art von Schleife auftritt.

  1. Benutzer ändern Eingang im Widget
  2. Widget Wechselereignis emittiert (SCROLL/Eingabe geklickt/Maus Urlaub usw.)
  3. -Controller reagiert, übersetzt in dem Modell
  4. Modell emittiert Ereignis ändern
  5. -Controller reagiert, ändert Wert in Widget
  6. Wertänderungsereignis ausgestrahlt wird, aber nicht hörte von Controller
+0

Danke für den Vorschlag. Leider ist dies nicht immer möglich, abhängig vom GUI-Toolkit. –

5

Die QT-Standardmethode dafür und auch die, die in ihrem sehr nützlichen Lernprogramm vorgeschlagen wird, besteht darin, die Änderung des Werts im Controller nur vorzunehmen, wenn der neue Wert vom aktuellen Wert abweicht.
Dies haben Art und Weise Signale, die die Semantik von valueChanged()

see this tutorial

1

Flags updaing Arbeit anzuzeigen. Sie können sie in Methoden wie BeginUpdate und EndUpdate einschließen.

+0

Ist das eine gängige Praxis, oder machen Sie nur eine fundierte Vermutung? – Statement

+0

Ich habe Flags verwendet, um den Aktualisierungsstatus in MFC Windows-Entwicklung anzuzeigen, die ein Dokument mit multipler View-Architektur hatte. In letzter Zeit habe ich bei der Entwicklung von .Net-Fenstern Flags verwendet, um anzuzeigen, ob das Auswahländerungsereignis während der Initialisierung ausgelöst wurde und ignoriert werden konnte. – Leah