11.4 Ereignisse grafischer Windows-Komponenten
Programme mit grafischer Benutzeroberfläche (GUI – Graphical User Interface) verwenden ein ereignisorientiertes Eingabemodell, bei dem nach der Initialisierung des Programms sämtliche Operationen nur noch als Reaktionen auf Ereignisse ausgeführt werden. Jeder Eingabe ist mindestens ein Ereignis zugeordnet, das wiederum mit einer Methode, dem Ereignishandler, verknüpft sein kann, aber nicht verknüpft sein muss.
Bei Auslösung des Ereignisses wird die registrierte Methode aufgerufen. Erst wenn die Ausführung des Ereignishandlers beendet ist, kann das nächste anstehende Ereignis seinen Ereignishandler aufrufen. Solange kein Ereignis ausgelöst wird, das es zu verarbeiten gilt, verharrt die Laufzeitumgebung im Ruhezustand und wartet auf das nächste Ereignis. Verantwortlich für den scheinbaren Ruhezustand ist die Nachrichtenschleife, die beim Aufruf der Methode Application.Run eingerichtet wird und permanent auf Ereignisse wartet.
11.4.1 Syntax und Nomenklatur
Rufen wir uns noch einmal ganz allgemein in Erinnerung, wie wir mit Visual Basic eine Methode durch den Compiler oder zur Laufzeit an ein bestimmtes Ereignis eines Objekts binden (für weitere Möglichkeiten siehe Abschnitt 3.10, »Ereignisse«):
Sub handler(<parameter>) Handles objekt.ereignis AddHandler objekt.ereignis, New Delegattyp(AddressOf handler)
Der Typ eines Ereignisses ist immer ein Delegate, zum Beispiel EventHandler für ein Click-Ereignis.
Public Delegate Sub EventHandler(sender As Object, e As EventArgs) |
Eine Methode, die das Ereignis behandeln soll, muss daher eine Parameterliste definieren, die aus zwei Parametern besteht. Der erste Parameter ist vom Typ Object, der zweite vom Typ EventArgs. Eine akzeptable Methodensignatur könnte in diesem Fall demnach
Private Sub MeineMethode(ByVal obj As Object, ByVal e As EventArgs)
sein, die beispielsweise mit
AddHandler MyComponent.Click, New EventHandler(AddressOf MeineMethode)
an das Click-Ereignis der Komponente MyComponent gebunden wird.
Nehmen wir die Parameterliste des Delegates genauer unter die Lupe. Der erste Parameter beschreibt die Referenz auf ein Objekt. Der Typ ist mit Object so allgemein gehalten, dass hier jeder Typ des .NET Frameworks infrage kommt. Der Name sender des Parameters deutet an, dass hier die Referenz auf das ereignisauslösende Objekt übergeben wird. Das ist sinnvoll, denn der Ereignishandler steht zu dem auslösenden Objekt in einer 1:n-Beziehung, da mit dem AddHandler-Operator die Ereignisse verschiedener Objekte mit demselben Ereignishandler verknüpft werden können. Über den ersten Parameter erhält die ereignisbehandelnde Methode also direkten Kontakt zur Ereignisquelle, kann sie möglicherweise abfragen und sogar manipulieren.
Der zweite Parameter im Delegate EventHandler ist vom Typ EventArgs aus dem Namensraum System. Er gibt dem Ereignisempfänger Zustandsinformationen über das Ereignis. Die Klasse EventArgs selbst enthält keine spezifischen Ereignisdaten. Andere Ereignisse können dem Ereignishandler aber durchaus Zusatzinformationen zur Verfügung stellen. Dann muss der zweite Parameter konventionsgemäß ein von EventArgs abgeleiteter Typ sein. Die ereignisspezifischen Daten sind die Eigenschaften des zweiten Parameters.
Hinweis |
Das .NET Framework hat für Ereignisse der Steuerelemente eine einheitliche Namenskonvention. Jedes Delegate wird als EventHandler bezeichnet, und der Bezeichner des zweiten Parameters tauscht das Suffix Handler gegen Args aus. Bei abgeleiteten Typen wird der Typbezeichnung des Delegates und der des Args-Parameters ein beschreibendes Präfix vorangestellt. Beispielsweise sind die Mausereignisse vom Typ des Delegates MouseEventHandler; der Parameter ist dann analog vom Typ MouseEventArgs. |
Das Ereignis Click des Buttons soll nach der oben codierten Verknüpfungsanweisung an eine Methode namens MeineMethode gebunden werden, deren Parameterliste der des Delegates EventHandler entspricht:
Private Sub MeineMethode(ByVal obj As Object, ByVal e As EventArgs) ... End Sub
Der Name der Methode ist beliebig. Der Automatismus der Entwicklungsumgebung setzt den Bezeichner des Ereignishandlers aus dem Namen des Steuerelements und – durch einen Unterstrich getrennt – dem Bezeichner des Ereignisses zusammen, beispielsweise:
Private Sub btnKopieren_Click(ByVal obj As Object, ByVal e As EventArgs)
Diese automatische Namensvergabe von .NET dient einer guten Les- und Wartbarkeit des Programmcodes, ist aber nicht zwingend. Auch wenn durch den Bezeichner des Ereignishandlers suggeriert wird, dass die Methode das Click-Ereignis des Objekts btn bedient, kann die Methode mit jedem x-beliebigen Ereignis verknüpft werden – sogar unabhängig vom zweiten Parameter, denn dieser ist vom Typ EventArgs deklariert, der selbst die Basisklasse aller anderen Klassen ist, die Ereignisdaten bereitstellen.
11.4.2 Ereignisse mit Ereignisdaten
Click hat keine spezifischen Ereignisdaten, andere Ereignisse haben diese aber durchaus. Wenn Sie zum Beispiel den Mauszeiger über den Clientbereich eines Formulars oder eines Steuerelements bewegen, werden in sehr schneller Abfolge MouseMove-Ereignisse ausgelöst:
Public Event MouseMove As MouseEventHandler
Der Ereignistyp ist nicht mehr das Delegate EventHandler, sondern MouseEventHandler:
Public Delegate Sub MouseEventHandler( _
ByVal sender As Object, ByVal e As MouseEventArgs) |
Eine Methode, die MouseMove-Ereignisse behandeln soll, empfängt im ersten Parameter ebenfalls die Referenz auf das ereignisauslösende Objekt und im zweiten Parameter die Referenz auf ein Objekt vom Typ der Klasse MouseEventArgs, das ereignisspezifische Zustandsdaten enthält.
Mit einem Ereignishandler auf dieses Ereignis zu reagieren ist dann sinnvoll, wenn die aktuelle Position des Mauszeigers ausgewertet werden muss. Eine MouseEventArgs-Instanz veröffentlicht zwei Eigenschaften, die die aktuelle Position wiedergeben: X und Y (siehe Tabelle 11.3).
Eigenschaft | Beschreibung |
X |
x-Koordinate des Mauszeigers |
Y |
y-Koordinate des Mauszeigers |
Das folgende Codefragment zeigt, wie Sie diese Daten auswerten können. Es handelt sich bei der Methode um den Ereignishandler des MouseMove-Ereignisses der Form. Zur Laufzeit bewirkt dieser Code, dass in der Titelleiste permanent die aktuelle x- und y-Position angezeigt wird, während Sie den Mauszeiger über der Form ziehen.
Private Sub TextboxKopieren_MouseMove( _ ByVal sender As Object, ByVal e As MouseEventArgs) Handles Me.MouseMove Me.Text = "X=" & e.X & " / Y=" & e.Y End Sub
11.4.3 Ereignisbehandlung in Visual Studio
Sie können Ereignisse an jeder Stelle im Programmcode an Ereignishandler binden oder, wenn erforderlich, diese wieder lösen. In den meisten Fällen werden Sie aber den Komfort der Entwicklungsumgebung nutzen wollen, die Ihnen alle denkbaren Möglichkeiten bietet.
Ein Doppelklick im Forms-Designer bewirkt, dass Visual Studio eine Methode für das Standardereignis der angeklickten Komponente erstellt. Um abweichend vom Standardereignis ein anderes zu behandeln, können Sie das Eigenschaftsfenster einsetzen, nachdem Sie in der Symbolleiste auf die Ereignisliste umgeschaltet haben (siehe Abbildung 11.4). Klicken Sie doppelt auf ein Ereignis in der Ereignisliste, das Sie behandeln wollen, wird automatisch ein Ereignishandler erstellt, dessen Bezeichner sich aus Objektnamen und Ereignis zusammensetzt. Die Bindung des Ereignishandlers erfolgt statisch mit der Handles-Klausel. Sie können dem Ereignishandler auch eine Bezeichnung geben, die von der üblichen Vorgabe abweicht. Dazu tragen Sie nur den von Ihnen bevorzugten Methodennamen in der Wertespalte neben dem Ereignis manuell ein.
Möchten Sie einen Ereignishandler gleichzeitig für mehrere Ereignisse registrieren, ist das auch sehr einfach zu lösen. Wenn Sie die Wertespalte zu einem Ereignis in der Ereignisliste aktivieren, sehen Sie eine Schaltfläche mit einem Pfeilsymbol. Sie können darüber eine Liste öffnen, in der alle Ereignishandler aufgeführt sind, die im Args-Parameter denselben Typ haben. Wählen Sie daraus den Ereignishandler aus, der das markierte Ereignis verarbeiten soll.
Mausbewegungen
Unsere oben entwickelte Windows-Anwendung wird nun um die Verarbeitung von MouseMove-Ereignisse der Form und der Kontrollelemente erweitert. In der Titelleiste der Form werden die Komponente und die x- und y-Koordinaten angezeigt. Da das Verhalten der Anwendung bei allen drei Mausereignissen identisch ist, drängt sich ein gemeinsamer Ereignishandler förmlich auf.
'...\WinForm\Ereignisse\MouseMove.vb |
Public Class MouseMove Private Sub Koord(ByVal sender As Object, ByVal e As MouseEventArgs) _ Handles MyBase.MouseMove, txtOriginal.MouseMove, txtKopie.MouseMove, _ btnKopieren.MouseMove, btnBeenden.MouseMove Dim titel As String = "" If sender Is Me Then : titel = "Form / " ElseIf sender Is btnKopieren Then : titel = "Kopieren / " ElseIf sender Is btnBeenden Then : titel = "Beenden / " ElseIf sender Is txtOriginal Then : titel = "Original / " ElseIf sender Is txtKopie Then : titel = "Kopie / " End If Me.Text = titel & "x = " & e.X & ", y = " & e.Y End Sub ... End Class
Über sender können wir auswerten, welche Komponente das Ereignis ausgelöst hat. Dazu wird der erste Parameter mit Me beziehungsweise den Referenzen auf ein Kontrollelement verglichen.
Als Ursprung der Koordinatenmessung ist standardmäßig der linke obere Eckpunkt des Clientbereichs der jeweiligen Komponente festgelegt. Deshalb macht bei der Ausführung des Programms die Koordinatenanzeige auch einen Zahlensprung, sobald der Mauszeiger aus dem Clientbereich der Form in den Clientbereich der Schaltfläche oder umgekehrt wechselt.
11.4.4 Ereignisbehandlung mit OnEreignis-Methoden
Alternativ zur Registrierung von Ereignishandlern können Sie auch die zu einem Ereignis korrespondierende OnEreignis-Methode neu definieren. Das folgende Listing zeigt beispielhaft die Methode des Click-Ereignisses der Klasse System.Windows.Forms.Control, der Basis aller grafischen Windows-Klassen.
<EditorBrowsable(EditorBrowsableState.Advanced)> _ Protected Overridable Sub OnClick(ByVal e As EventArgs) Dim handler As EventHandler = _ DirectCast(MyBase.Events.Item(Control.EventClick), EventHandler) If (Not handler Is Nothing) Then handler.Invoke(Me, e) End Sub
Innerhalb der Methode werden die beim Objekt registrierten Handler für das Ereignis aufgerufen. Das Überschreiben der Methode kann also den Aufruf der Ereignishandler ersetzen.
Methoden, deren Bezeichner vor dem Ereignisnamen das Präfix On aufweisen, sind für den Aufruf aller installierten Ereignishandler zuständig. |
Interessant ist die Parameterliste, die nur aus dem Argument besteht, das spezifische Zustands-informationen des Ereignisses bereitstellt. Was fehlt, ist das Argument, das die Referenz auf den Auslöser liefert. Das bedeutet, dass alle OnXxx-Methoden immer auf die aktuelle Komponente aufgerufen werden. Es sind Ereignisempfänger, die spezifisch für ein ganz bestimmtes Objekt sind und nicht von einer anderen Komponente gleichzeitig zur Behandlung eines ausgelösten Ereignisses benutzt werden können. In der Basisklasse Control sind alle OnEreignis-Methoden als Overridable deklariert und müssen deshalb mit Overrides überschrieben werden.
Wir entwickeln eine von Form abgeleitete Klasse, die die OnClick-Methode der Basisklasse überschreibt und beim Klicken auf den Clientbereich den Text »... in der OnClick-Methode« in die Titelleiste des Formulars schreibt. Parallel wird ein Ereignishandler installiert, der zusätzlich mit einer ergänzenden Titelleistenanzeige auf das Ereignis Click reagieren soll.
'...\WinForm\Ereignisse\OnClick.vb |
Public Class OnClick Protected Overrides Sub OnClick(ByVal e As EventArgs) ' MyBase.OnClick(e) Me.Text += " ... in der OnClick-Methode" End Sub Private Sub OnClick_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles MyBase.Click CType(sender, OnClick).Text += " ... im Ereignishandler " End Sub End Class
Beachten Sie bitte, dass eine Anweisung in OnClick auskommentiert ist.
Starten Sie diese Anwendung, wird in die Titelleiste zwar der Text »... in der OnClick-Methode« geschrieben, aber der Text, der aus dem Click-Ereignishandler ebenfalls in die Titelleiste geschrieben werden soll, wird definitiv nicht angezeigt. Der Ereignishandler wird also überhaupt nicht aufgerufen.
Heben Sie die Kommentierung in OnClick auf, lauetet die Anzeige in der Titelleiste »Im Ereignishandler ... in der OnClick-Methode«. Mit MyBase.OnClick() wird die ursprüngliche Funktionalität wiederhergestellt: der Aufruf der registrierten Ereignishandler. Dieser Aufruf wird in den meisten Fällen das Mittel der Wahl sein, um durch das Überschreiben die Möglichkeiten zu ergänzen und nicht zu ersetzen.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.