Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einführung
2 Grundlagen der Sprachsyntax
3 Klassendesign
4 Weitere Datentypen
5 Multithreading
6 Collections und LINQ
7 Eingabe und Ausgabe
8 Anwendungen: Struktur und Installation
9 Code erstellen und debuggen
10 Einige Basisklassen
11 Windows-Anwendungen erstellen
12 Die wichtigsten Steuerelemente
13 Tastatur- und Mausereignisse
14 MDI-Anwendungen
15 Grafiken mit GDI+
16 Drucken
17 Entwickeln von Steuerelementen
18 Programmiertechniken
19 WPF – Grundlagen
20 Layoutcontainer
21 WPF-Steuerelemente
22 Konzepte von WPF
23 Datenbankverbindung mit ADO.NET
24 Datenbankabfragen mit ADO.NET
25 DataAdapter
26 Offline mit DataSet
27 Datenbanken aktualisieren
28 Stark typisierte DataSets
A Anhang: Einige Übersichten
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual Basic 2008 von Andreas Kuehnel, Stephan Leibbrandt
Das umfassende Handbuch
Buch: Visual Basic 2008

Visual Basic 2008
3., aktualisierte und erweiterte Auflage, geb., mit DVD
1.323 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1171-0
Pfeil 3 Klassendesign
Pfeil 3.1 Objektorientierung
Pfeil 3.1.1 Einführung
Pfeil 3.1.2 Vorteile
Pfeil 3.1.3 Klassenimplementierung in .NET
Pfeil 3.1.4 Klassen in Visual Basic
Pfeil 3.1.5 Projekttyp »Klassenbibliothek«
Pfeil 3.1.6 Bemerkung zu den Codefragmenten
Pfeil 3.1.7 Objekte durch New
Pfeil 3.1.8 Ausnahmen mit Throw auslösen
Pfeil 3.1.9 Datentypen
Pfeil 3.1.10 Sichtbarkeit der Klasse
Pfeil 3.1.11 Aufteilung der Definition mit Partial
Pfeil 3.1.12 Grafikbibliothek: Beispiel für Kapitel 3 und 4
Pfeil 3.2 Kapselung
Pfeil 3.2.1 Kombinationen
Pfeil 3.2.2 Private
Pfeil 3.2.3 Sichtbarkeitsmodifikatoren
Pfeil 3.2.4 Lokale Variablen
Pfeil 3.2.5 Softwareschutz
Pfeil 3.2.6 Grafikbibliothek: private Größe des Rechtecks
Pfeil 3.3 Verhalten (Methoden)
Pfeil 3.3.1 Prinzip
Pfeil 3.3.2 Verlassen der Methode
Pfeil 3.3.3 Parameter
Pfeil 3.3.4 Überladung (Overloads)
Pfeil 3.3.5 Rückgabewert
Pfeil 3.3.6 Reine Deklaration mit Partial
Pfeil 3.3.7 Grafikbibliothek: Zugriffsmethoden auf die Größe des Rechtecks
Pfeil 3.4 Bindung
Pfeil 3.4.1 Klassenbindung mit Shared
Pfeil 3.4.2 Klassenkonstruktoren
Pfeil 3.4.3 Externe Funktionen
Pfeil 3.4.4 Grafikbibliothek: Rechteckübergreifendes
Pfeil 3.5 Objektinitialisierung mit Konstruktoren
Pfeil 3.5.1 Objektkonstruktoren
Pfeil 3.5.2 Nichtöffentliche Konstrukturen
Pfeil 3.5.3 Grafikbibliothek: Initialisierung des Rechtecks
Pfeil 3.6 Zustände (Felder)
Pfeil 3.6.1 Deklaration und Initialisierung
Pfeil 3.6.2 Sichtbarkeit
Pfeil 3.6.3 Konstanten: ReadOnly und Const
Pfeil 3.6.4 With
Pfeil 3.6.5 Grafikbibliothek: Konstanten des Rechtecks
Pfeil 3.7 Eigenschaften
Pfeil 3.7.1 Kontrolle beim Aufrufer
Pfeil 3.7.2 Zugriffsmethoden
Pfeil 3.7.3 Getter und Setter: Property
Pfeil 3.7.4 Indexer
Pfeil 3.7.5 Standardeigenschaft: Default
Pfeil 3.7.6 Schreibschutz und Leseschutz: ReadOnly und WriteOnly
Pfeil 3.7.7 Sichtbarkeit
Pfeil 3.7.8 Klammern
Pfeil 3.7.9 Grafikbibliothek: Eigenschaften des Rechtecks
Pfeil 3.8 Innere Klassen
Pfeil 3.8.1 Beziehung zur äußeren Klasse
Pfeil 3.8.2 Sichtbarkeit
Pfeil 3.8.3 Grafikbibliothek: Position des Rechtecks
Pfeil 3.9 Dynamisches Verhalten: Delegate und Function
Pfeil 3.9.1 Funktionszeiger: Delegates
Pfeil 3.9.2 Automatisch generierter Code
Pfeil 3.9.3 Mehrere Aktionen gleichzeitig
Pfeil 3.9.4 Asynchrone Aufrufe
Pfeil 3.9.5 Funktionsobjekte: Function (λ-Ausdrücke)
Pfeil 3.9.6 Umwandlungen
Pfeil 3.9.7 Grafikbibliothek: Vergleich von Rechtecken
Pfeil 3.10 Ereignisse
Pfeil 3.10.1 Ereignis: Event und RaiseEvent
Pfeil 3.10.2 Statische Methodenbindung WithEvents und Handles
Pfeil 3.10.3 Dynamische Methodenbindung: AddHandler und RemoveHandler
Pfeil 3.10.4 Benutzerdefinierte Ereignisse: Custom Event
Pfeil 3.10.5 Umwandlungen
Pfeil 3.10.6 Grafikbibliothek: Größenänderungen von Rechtecken überwachen
Pfeil 3.11 Benutzerdefinierte Operatoren
Pfeil 3.11.1 Prinzip
Pfeil 3.11.2 Überladung
Pfeil 3.11.3 Vergleich
Pfeil 3.11.4 Typumwandlung mit CType: Widening und Narrowing
Pfeil 3.11.5 Wahrheitswerte: IsTrue und IsFalse
Pfeil 3.11.6 Grafikbibliothek: Addition und Umwandlung von Rechtecken
Pfeil 3.12 Alle Klassenelemente
Pfeil 3.12.1 Der Namensraum My
Pfeil 3.13 Vererbung
Pfeil 3.13.1 Klassenbeziehung durch Inherits
Pfeil 3.13.2 Sichtbarkeitsmodifikatoren
Pfeil 3.13.3 Zugriff auf Eltern mit MyBase
Pfeil 3.13.4 Modifikation: Shadows und Overloads (Overrides)
Pfeil 3.13.5 Abstrakte Klassen: MustInherit und MustOverride
Pfeil 3.13.6 Spezifische Catch-Blöcke
Pfeil 3.13.7 Eigene Ausnahmen
Pfeil 3.13.8 Arrays
Pfeil 3.13.9 Grafikbibliothek: neue Vielecke
Pfeil 3.14 Polymorphie
Pfeil 3.14.1 Virtuelle Methoden: Overridable und Overrides
Pfeil 3.14.2 Unterbrechen: Overloads, Shadows und Overrides
Pfeil 3.14.3 Unterbinden: NotInheritable, NotOverridable und MyClass
Pfeil 3.14.4 Konstruktoren
Pfeil 3.14.5 Equals
Pfeil 3.14.6 ToString
Pfeil 3.14.7 Grafikbibliothek: Objektsammlungen
Pfeil 3.15 Schnittstellen: Interface und Implements
Pfeil 3.15.1 Benennungen und Parameter
Pfeil 3.15.2 Schnittstellen und Vererbung
Pfeil 3.15.3 Schnittstellenvererbung
Pfeil 3.15.4 Schnittstelle oder abstrakte Klasse?
Pfeil 3.15.5 Grafikbibliothek: Flächen
Pfeil 3.16 Lebensende eines Objekts
Pfeil 3.16.1 Garbage Collector
Pfeil 3.16.2 Destruktoren
Pfeil 3.16.3 Dispose und Using


Rheinwerk Computing - Zum Seitenanfang

3.10 Ereignisse Zur nächsten ÜberschriftZur vorigen Überschrift

»Kein Schwein ruft mich an …« (Max Raabe und das Palast-Orchester, 1992)

Kein Wunder, dass keine Anrufe eintreffen. Haben Sie schon jemandem Bescheid gesagt, wie Sie erreichbar sind? Alle bisherigen Programme gingen davon aus, dass Sie sich selbst darum kümmern, woran Sie interessiert sind. Da dies auf Dauer recht mühsam ist, hat Visual Basic einen Mechanismus, mit dem ein Objekt Interesse bekunden kann, wenn etwas Bestimmtes passiert. Dazu meldet es sich bei dem Objekt an, das die Aktion von Interesse durchführt. Danach wird bei jeder durchgeführten Aktion der Interessent benachrichtigt. Er muss nicht ständig selbst auf der Hut sein, sondern kann in Ruhe abwarten, bis etwas passiert: Er wird automatisch benachrichtigt. Dies hat noch einen weiteren Effekt: Nicht jeder muss allem zuhören. Stellen Sie sich vor, Sie müssten täglich alle im Fernsehen ausgestrahlten Sendungen sehen. Das entspricht der bisherigen Situation. Glücklicherweise gibt es eine Fernbedienung, mit der Sie als Zuschauer festlegen, was Sie empfangen wollen. Damit wir dies in Visual Basic realisieren können, brauchen wir:

  • die Definition der Aktion von Interesse (Ereignis)
  • die Registrierung von Interessenten (Ereignishandler)
  • eine Aktion, die alle registrierten Interessenten benachrichtigt (Auslösen)

Abhängig von der Art der Registrierung wird zwischen statischer und dynamischer Bindung unterschieden. Wenn die Standardmechanismen nicht reichen, helfen benutzerdefinierte Ereignisse weiter.


Hinweis
Die beiden Registrierungsarten (siehe Abschnitt 3.10.2, »Statische Methodenbindung WithEvents und Handles« und Abschnitt 3.10.3, »Dynamische Methodenbindung: AddHandler und RemoveHandler«) können gemischt werden.



Hinweis
Das im Ereignis implizit enthaltene Objekt kann nicht vom Garbage Collector aufgeräumt werden. Daher ist eine Deregistrierung von nicht mehr benötigten Ereignissen empfehlenswert.



Rheinwerk Computing - Zum Seitenanfang

3.10.1 Ereignis: Event und RaiseEvent Zur nächsten ÜberschriftZur vorigen Überschrift

Als Erstes schauen wir uns die Syntax an, mit der ein Ereignis deklariert wird. Dazu gibt es zwei Varianten. Die Definition eines Delegates ist in Abschnitt 3.9.1, »Funktionszeiger: Delegate«, beschrieben. Optionale Teile sind in eckige Klammern gesetzt und kursive Teile müssen Sie Ihren Bedürfnissen anpassen.


[<Modifikatoren>] Event Name As Delegate-Typ [Effekt]

[<Modifikatoren>] Event Name([<Parameter>]) [Effekt]


Hinweis
Ein Ereignis wird nur deklariert, eine Wertzuweisung nimmt der Compiler automatisch vor (siehe den Unterabschnitt »Automatisch erzeugter Code«).


Ein Ereignis kann durch die Modifikatoren in Tabelle 3.14 angepasst werden und darf einen der Effekte in Tabelle 3.15 haben.


Tabelle 3.14 Modifikatoren eines Ereignisses

Art Beschreibung

Sichtbarkeit

Grad der Öffentlichkeit (siehe Abschnitt 3.2, »Kapselung«)

Bindung

Objekt- oder klassenbezogen (siehe Abschnitt 3.4, »Bindung«)

Redefinition

Ersatz einer Definition mit Shadows (siehe Abschnitt 3.13, »Vererbung«)



Tabelle 3.15 Effekte eines Ereignisses

Art Beschreibung

Implementation

Erfüllung einer Schnittstelle (siehe Abschnitt 3.15, »Schnittstellen: Interface und Implements«)


Anders als bei Delegates wird ein Ereignis nicht direkt angesprochen, sondern »ausgelöst«. Für Standard- und benutzerdefinierte Ereignisse gilt dieselbe Syntax (kursive Teile müssen Sie Ihren Bedürfnissen anpassen):


RaiseEvent Name([<Parameter>])


Hinweis
Ein Ereignis kann nur in derselben Klasse ausgelöst werden, in der es deklariert ist. Dies gilt auch im Rahmen der Vererbung. Fehlt in einer Klasse mit einem Ereignis das korrespondierende RaiseEvent, ist eine Auslösung nur über Invoke() möglich (siehe den Unterabschnitt »Automatisch erzeugter Code«).


Damit kann eine erste Anwendung mit Ereignissen erstellt werden. Sie macht noch nichts Sichtbares, da für das Ereignis noch keine Interessenten registriert worden sind.


'...\Klassendesign\Ereignisse\Ereignis.vb

Option Strict On 
Namespace Klassendesign 
  Class Einfach

    Event Ereignis(ByVal no As Integer)

    Sub Auslösung() 
      RaiseEvent Ereignis(-3) 
    End Sub

    Shared Sub Test() 
      Dim ein As Einfach = New Einfach() 
      ein.Auslösung() 
      Console.ReadLine() 
    End Sub 
  End Class 
  ... 
End Namespace

Hinweis
Die Behandlung eines Ereignisses ist optional. Unterbleibt sie, passiert einfach »nichts«.


Ereignisse und Delegates

Durch Event x() wird implizit ein Delegate definiert. Daher kann das vorige Beispiel auch ein Delegate identischer Signatur verwenden, wie das nächste Codefragment zeigt. Die Verwendung von Sub für ein solches Delegate ist zwingend. Ob Sie den Weg über das Delegate gehen oder ein Ereignis direkt spezifizieren, ist reine Geschmackssache.


'...\Klassendesign\Ereignisse\Ereignis.vb

Option Strict On 
Namespace Klassendesign 
  ... 
  Class Delagat

    Delegate Sub Delegat(ByVal no As Integer) 
    Event Ereignis As Delegat

    Sub Auslösung() 
      RaiseEvent Ereignis(-3) 
    End Sub

    Shared Sub Test() 
      Dim del As Delagat = New Delagat() 
      del.Auslösung() 
      Console.ReadLine() 
    End Sub 
  End Class 
End Namespace

Hinweis
Ein Function-Delegate mit Rückgabewert kann nicht als Typ für ein Ereignis verwendet werden.


Automatisch erzeugter Code

Die Deklaration eines Ereignisses definiert implizit ein paar Klassenmitglieder:

Private EreignisEvent As EreignisEventHandler 
Sub add_Ereignis(ByVal obj As EreignisEventHandler) 
  ... 
End Sub 
Sub remove_Ereignis(ByVal obj As EreignisEventHandler) 
  ... 
End Sub 
Shared __ENCList As List(Of WeakReference) 
Class EreignisEventHandler                   '< MulticastDelegate < Delegate 
  ... 
End Class

Hinweis
Über eine Typanalyse mit den Klassen aus dem Namensraum Reflection ist ein Zugriff auch dann möglich, wenn der Compiler einen direkten Zugriff verbietet.


Die automatisch erzeugten Klassenmitglieder können bis auf die Klasse nicht direkt benutzt werden. Wenn Sie versuchen, gleichnamige Klassenmitglieder zu definieren, bekommen Sie dennoch eine Fehlermeldung des Compilers. Wie bei allen Automatismen sollten Sie sich nicht darauf verlassen, dass eine zukünftige Version von Visual Basic dieselben Mitglieder definiert. Intern verwendet RaiseEvent die Invoke()-Methode des implizit definierten Delegates. Das folgende Codefragment vermittelt eine Idee davon, wie das passieren könnte. Bitte beachten Sie, dass der Typ EreignisEventHandler nicht im Quelltext definiert wird. Er steht implizit zur Verfügung.


'...\Klassendesign\Ereignisse\RaiseEvent.vb

Option Strict On 
Namespace Klassendesign 
  Class Umstandskrämer 
    Event Ereignis(ByVal no As String)

    Sub Auslösung() 
      RaiseEvent Ereignis("normal") 
      Dim del As EreignisEventHandler = AddressOf Empfänger 
      del.Invoke("umständlich") 
    End Sub

    Sub Empfänger(ByVal art As String) Handles Me.Ereignis 
      Console.WriteLine("Art {0}", art) 
    End Sub

    Shared Sub Test() 
      Dim man As Umstandskrämer = New Umstandskrämer() 
      man.Auslösung() 
      Console.ReadLine() 
    End Sub 
  End Class 
End Namespace

Die Ausgabe zeigt die zweifache Ansprache der Ereignisbehandlungsprozedur:

Art normal 
Art umständlich

Hinweis
Die Benutzung der Klasse ist nicht von diesem Automatismus betroffen.



Rheinwerk Computing - Zum Seitenanfang

3.10.2 Statische Methodenbindung WithEvents und Handles Zur nächsten ÜberschriftZur vorigen Überschrift

Um nun dem Ereignis Leben einzuhauchen, wird eine Ereignisbehandlung festgelegt. Am einfachsten (und unflexibelsten) ist es, bereits im Quelltext durch Deklaration festzulegen, welche Methode sich um ein Ereignis kümmert. Die Syntax lautet wie folgt (optionale Teile stehen in eckigen Klammern, kursive Teile müssen Sie Ihren Bedürfnissen anpassen, der Unterstrich macht wie üblich aus zwei Quelltextzeilen eine logische Zeile):


[<Modifikatoren>] Sub Name([<Parameter>]) _ 
Handles Kontext.Ereignis [, Kontext.Ereignis, ...] 
  [<Anweisungen>] 
End Sub

[<Modifikatoren>] Function Name([<Parameter>]) As Typ _ 
Handles Kontext.Ereignis [, Kontext.Ereignis, ...] 
  [<Anweisungen>] 
End Function


Hinweis
Die Behandlung eines Ereignisses ist optional, und an einem Ereignis ohne korrespondierendes Handles ist nichts auszusetzen. Unglücklich ist dagegen ein Handles an der das Ereignis auslösenden Methode, da es eine infinite Rekursion auslöst.


Tabelle 3.16 zeigt die erlaubten Modifikatoren.


Tabelle 3.16 Modifikatoren einer Ereignisbehandlungsmethode

Art Beschreibung

Sichtbarkeit

Grad der Öffentlichkeit (siehe Abschnitt 3.2, »Kapselung«)

Bindung

Objekt- oder klassenbezogen (siehe Abschnitt 3.4, »Bindung«)

Redefinition

Art des Ersatzes oder Zwangs zu einer Definition (siehe Abschnitt 3.13, »Vererbung« und Abschnitt 3.3.4, »Überladung (Overloads)«)

Leer

Ohne Anweisungen (siehe Abschnitt 3.3.6, »Reine Deklaration mit Partial«), korrespondierende Implementation erforderlich.


Der Kontext des Ereignisses kann nicht beliebig sein. Es muss das Objekt oder die Klasse sein, die entweder selbst sowohl eine Methode als auch ein Ereignis enthält (Me, MyClass) oder auf einer Klasse basiert, die dies tut (MyBase). Dabei darf keine Objektreferenz oder ein Klassenname verwendet werden, auch wenn sie inhaltlich dasselbe sein sollten. Auf die einzige weitere Alternative in Tabelle 3.17 gehen wir etwas weiter unten ein.


Tabelle 3.17 Mögliche Kontexte eines Ereignisses in Handles

Kontext Beschreibung

Me

aktuelles Objekt, nur hier: aktuelle Klasse

MyClass

aktuelle Klasse, nur hier: aktuelles Objekt

MyBase

Elternklasse (siehe Abschnitt 3.13, »Vererbung«)

withevents

Variable, die mit WithEvents gekennzeichnet ist (siehe unten)


Das Ereignis ist der im vorigen Abschnitt mit Event deklarierte Bezeichner. Das folgende Codefragment zeigt die Reaktion eines Zuschauers auf eine Werbeunterbrechung eines Films. Die Reihenfolge der Auswertung ist durch Zahlen kenntlich gemacht. Als Erstes wird ein neuer Film erstellt und im zweiten Schritt mit der Methode Pause() unterbrochen. In dieser wird als dritter Schritt das Ereignis Werbung() mit RaiseEvent ausgelöst. Dies hat zur Folge, dass für jede Handles-Deklaration, die das Ereignis Werbung() als zu behandeln kennzeichnet, die Methode aufgerufen wird, die mit dem Handles gekennzeichnet ist. Da Zuschauer() zweifach markiert ist, wird die Methode auch zweimal aufgerufen werden. Es ist egal, ob Sie das aktuelle Objekt, hier ist es der aktuelle Film, mit Me oder MyClass qualifizieren. Schließlich beendet ReadLine() im fünften Schritt das Programm.


'...\Klassendesign\Ereignisse\Statisch.vb

Option Strict On 
Namespace Klassendesign 
  Class Film 
    Public Event Werbung()

    Sub Pause() 
      RaiseEvent Werbung()                '3 
    End Sub

    Sub Zuschauer() Handles Me.Werbung, MyClass.Werbung 
      Console.WriteLine("Umschalten!")    '4 
    End Sub

    Shared Sub Test() 
      Dim sendung As Film = New Film()    '1 
      sendung.Pause()                     '2 
      Console.ReadLine()                  '5 
    End Sub 
  End Class 
  ... 
End Namespace

Hinweis
Auch wenn alle Ereignisse und Methoden mit Shared klassengebunden sind, können Me und MyClass gleichwertig verwendet werden.


Die Ausgabe zeigt die zweifache Reaktion durch die zwei Ereignisse in der Handles-Klausel:

Umschalten! 
Umschalten!

Hinweis
Eine Methode mit einer Handles-Klausel kann auch weiterhin ganz normal aufgerufen werden.


Behandlung außerhalb der Klasse

Wenn Sie, was in der Praxis weit häufiger ist, ein Ereignis und dessen Behandlungsmethode in separaten Klassen haben, entfallen die drei Konstanten Me, MyClass und MyBase in der Handles-Klausel als Möglichkeit zur Spezifikation des Ereignisobjekts. Dann muss als Kontextangabe eine Objektreferenz angegeben werden, die wie folgt mit WithEvents als möglicher Emittent von Ereignissen gekennzeichnet wurde (optionale Teile stehen in eckigen Klammern und kursive Teile müssen Sie Ihren Bedürfnissen anpassen):


[<Modifikatoren>] WithEvents Name As typ [= Wert]


Hinweis
Der typ muss zwar eine Klasse oder Schnittstelle sein, darf aber ereignislos sein. Es wird nur die Möglichkeit eines Ereignisses deklariert.


Da ein Ereignis nur innerhalb der Klasse ausgelöst werden kann, in der es deklariert ist, ist die Initialisierung von Name bezüglich der Behandlungsmethoden gewährleistet (es käme sonst vorher zu einer NullReferenceException bei dem Versuch, über Name das Ereignis auszulösen). Sie können also Name auch außerhalb der Deklarationszeile initialisieren, Hauptsache es passiert vor der Ereignisauslösung. Die Modifikatoren sind fast deckungsgleich mit denen für ein Feld auf Klassenebene (siehe Tabelle 3.18).


Tabelle 3.18 Modifikatoren eines Ereignisemittents

Art Beschreibung

Sichtbarkeit

Grad der Öffentlichkeit (siehe Abschnitt 3.2, »Kapselung«)

Bindung

Objekt- oder klassenbezogen (siehe Abschnitt 3.4, »Bindung«)

Redefinition

Ersatz mit Shadows (siehe Abschnitt 3.13, »Vererbung«)



Hinweis
Insbesondere ReadOnly ist als Modifikator nicht erlaubt.


Das folgende Codefragment zeigt eine Separierung einer Lotto-Klasse mit den Ereignissen Zahl und Rest und einem Modul, das die Ereignisse behandelt. Dies zeigt, dass eine Klasse auch mehrere Ereignisse und Ereignisauslösungen im selben Block definieren kann. Sie können auch sehen, dass dasselbe Ereignis auch von verschiedenen Prozeduren behandelt werden kann.


'...\Klassendesign\Ereignisse\Statisch.vb

Option Strict On 
Namespace Klassendesign 
  ... 
  Class Lotto 
    Event Zahl(ByVal no As Short) 
    Event Rest(ByVal no As Short)

    Sub Ziehung(ByVal no As Short, ByVal rest As Short) 
      Console.WriteLine("Gezogen: {0}", no) 
      RaiseEvent Zahl(no) 
      RaiseEvent Rest(rest) 
    End Sub 
  End Class

  Module Statisch 
    WithEvents lotto As Lotto

    Sub Booch(ByVal no As Int32) Handles lotto.Zahl 
      Console.WriteLine("B: {0}", If(no=16, "gut", "Mist")) 
    End Sub

    Sub Jacobson(ByVal no As Int32) Handles lotto.Zahl 
      Console.WriteLine("J: {0}", If(no=41, "gut", "Mist")) 
    End Sub

    Sub Rumbaugh(ByVal no As Int32) Handles lotto.Rest 
      Console.WriteLine("R: noch {0} Zahlen", no) 
    End Sub

    Sub Test() 
      lotto = New Lotto() 
      lotto.Ziehung(16, 5) : lotto.Ziehung(41, 4) 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt die Reaktion der verschiedenen registrierten Prozeduren. Jede Handles-Deklaration führt zu einer eigenständigen Behandlung eines Ereignisses.

Gezogen: 16 
B: gut 
J: Mist 
R: noch 5 Zahlen 
Gezogen: 41 
B: Mist 
J: gut 
R: noch 4 Zahlen

Hinweis
Eine einmal im Quelltext deklarierte Ereignisbehandlung kann nicht zurückgenommen werden (auch nicht im Rahmen der Vererbung); weitere hinzuzufügen ist jedoch möglich.


Bindung an ein Objekt

Die Bindung einer Behandlung eines Ereignisses mittels der Handles-Klausel erfolgt bereits im Quelltext. Aber wann wird das Ereignis an ein Objekt gebunden? Während einer Initialisierung oder dynamisch? Das folgende Codefragment initialisiert ein Partner-Objekt während der Deklaration und löst dann in Test() mittels Treffen() ein Ereignis aus. Danach wird ein neuer Partner zugewiesen, und ein erneutes Treffen findet statt.


'...\Klassendesign\Ereignisse\Bindung.vb

Option Strict On 
Namespace Klassendesign 
  Class Partner 
    Dim name As String 
    Sub New(ByVal name As String) 
      Me.name = name 
    End Sub 
    Event Gruß(ByVal name As String) 
    Sub Treffen() 
      RaiseEvent Gruß(name) 
    End Sub 
  End Class

  Module Bindung 
    WithEvents p As Partner = New Partner("Primus")

    Sub Gruß(ByVal name As String) Handles p.Gruß 
      Console.WriteLine("Hallo {0}", name) 
    End Sub

    Sub Test() 
      p.Treffen() 
      p = New Partner("Secundus") 
      p.Treffen() 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt ganz klar, dass das Ereignisobjekt das zum Zeitpunkt der Ereignisauslösung gültige Objekt ist, die Bindung ist also trotz fester Handles-Klausel dynamisch.

Hallo Primus 
Hallo Secundus

Reihenfolge der Handler

Wenn es möglich ist, mehrere Prozeduren zur Behandlung einzusetzen, stellt sich die Frage, in welcher Reihenfolge sie zum Zuge kommen. Das folgende Codefragment zeigt eine Fütterung im Zoo. Die Tiere stellen sich mit ihren Handles-Klauseln in alphabetischer Reihenfolge an. Alle bis auf die Giraffe nehmen auch Wasser mit der Fütterung.


'...\Klassendesign\Ereignisse\ReihenfolgeHandles.vb

Option Strict On 
Namespace Klassendesign 
  Class Zoo 
    Event Futter(ByRef no As Short) 
    Event Wasser(ByRef no As Short) 
    Sub Fütterung() 
      Console.WriteLine("—Futter—") : RaiseEvent Futter(0) 
      Console.WriteLine("—Wasser—") : RaiseEvent Wasser(0) 
    End Sub

    Sub Affe(ByRef no As Short) Handles Me.Futter, Me.Wasser 
      no += 1S : Console.WriteLine("Affe: {0}", no) 
    End Sub 
    Sub Bär(ByRef no As Short) Handles Me.Futter, Me.Wasser 
      no += 1S : Console.WriteLine("Bär: {0}", no) 
    End Sub 
    Sub Giraffe(ByRef no As Short) Handles Me.Futter 
      no += 1S : Console.WriteLine("Giraffe: {0}", no) 
    End Sub 
    Sub Löwe(ByRef no As Short) Handles Me.Futter, Me.Wasser 
      no += 1S : Console.WriteLine("Löwe: {0}", no) 
    End Sub

    Shared Sub Test() 
      Dim zoo As Zoo = New Zoo() 
      zoo.Fütterung() 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Wenn Prozeduren beim Auslösen eines Ereignisses in der Reihenfolge ihrer Handles-Deklarationen im Quelltext aufgerufen würden, müssten die Tiere in der Ausgabe in alphabetischer Reihenfolge erscheinen. Die folgende Ausgabe zeigt, dass dies nicht der Fall ist. Die gleiche Reihenfolge bei Futter und Wasser lässt vermuten, dass die Ordnung durch die betroffenen Prozeduren bestimmt ist und nicht durch die Anzahl der Prozeduren, die ein Ereignis behandeln.

Futter-- 
Löwe: 1 
Bär: 2 
Giraffe: 3 
Affe: 4 
--Wasser-- 
Löwe: 1 
Bär: 2 
Affe: 3

Der Vollständigkeit halber folgt ein Codefragment, mit dem sich die Aufrufliste explizit ermitteln lässt:

Dim f As Reflection.FieldInfo = zoo.GetType().GetField("futterEvent", _ 
  Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic) 
Dim h As Zoo.futterEventHandler = _ 
  CType(f.GetValue(zoo), Zoo.futterEventHandler) 
For Each del As [Delegate] In h.GetInvocationList() 
  Console.WriteLine("Methode {0}", del.Method.ToString()) 
Next

Automatisch erzeugter Code

Durch die Deklaration

WithEvents obj As Ereignis

werden vom Compiler automatisch folgende Klassenmitglieder definiert beziehungsweise umgewidmet (alle Private):

Property obj(ByVal WithEventsValue As Ereignis) As Ereignis 
_obj As Ereignis 
Shared __ENCList As List(Of WeakReference)

Innerhalb der Eigenschaft wird die Registrierung des Ereignisses automatisch während einer Zuweisung vorgenommen. Sie erfolgt auch automatisch während der Laufzeit. Die entsprechenden Anweisungen werden vom Compiler automatisch generiert. Wenn Sie versuchen, eines der automatisch generierten Klassenmitglieder selbst zu definieren, bekommen Sie eine Fehlermeldung des Compilers.


Rheinwerk Computing - Zum Seitenanfang

3.10.3 Dynamische Methodenbindung: AddHandler und RemoveHandler Zur nächsten ÜberschriftZur vorigen Überschrift

Die im letzten Abschnitt gezeigte Bindung einer Methode an ein Ereignis durch einfache Deklaration ist zwar sehr komfortabel, aber auch unflexibel. Es gibt Situationen, in denen Sie zur Laufzeit die Kontrolle darüber brauchen, ob eine Methode durch ein Ereignis angesprochen wird. Im täglichen Leben kommt das zum Beispiel vor, wenn Sie Ihr Handy im Flugzeug abschalten. Ein Beispiel aus der Softwaretechnik ist eine Funktion, die in einem Fenster viele Änderungen gleichzeitig vornimmt. Es wäre nicht sinnvoll, das Fenster nach jeder kleinen Änderung neu zu zeichnen. Besser ist es, das Neuzeichnen temporär abzuschalten.

Zur Kontrolle der Bindung einer Methode an ein Ereignis wird die folgende Syntax verwendet (die kursiven Namen müssen Sie Ihren Bedürfnissen anpassen):


AddHandler EreignisObj, delegatObj 
RemoveHandler EreignisObj, delegatObj


Hinweis
Die Methode darf einen Rückgabewert haben. Er wird ignoriert.


Der erste Parameter, EreignisObj, bezeichnet ein mit Event deklariertes Ereignis, und DelegatObj ist ein Funktionszeiger auf die behandelnde Methode. Wie in Abschnitt 3.9.1, »Funktionszeiger: Delegates«, beschrieben wurde, kann DelegatObj in vier Varianten erzeugt werden. Da der Rückgabewert einer das Ereignis behandelnden Funktion ignoriert wird und ein Funktionsobjekt nur indirekt etwas »bewirken« kann, werden Sie die Varianten mit Function eher selten einsetzen. Der Typ des Delegates EreignisEventHandler wird, wie in Abschnitt 3.10.1, »Ereignis: Event und RaiseEvent«, beschrieben wurde, automatisch vom Compiler erzeugt (optionale Teile stehen in eckigen Klammern und kursive Teile müssen Sie Ihren Bedürfnissen anpassen).


Hinweis
Das im Ereignis implizit enthaltene Objekt kann nicht vom Garbage Collector aufgeräumt werden. Daher ist eine Deregistrierung von nicht mehr benötigten Ereignissen empfehlenswert.



New EreignisEventHandler(AddressOf Methode) 
New EreignisEventHandler(Function([<Parameter>]) Anweisung) 
AddressOf Methode 
Function([<Parameter>]) Anweisung

Als Beispiel folgt ein Haus, das mit einer Alarmanlage gesichert ist. Durch AddHandler wird sie scharf geschaltet, da dann eine Methode das Ereignis behandelt. Analog schaltet RemoveHandler sie ab, da die Ereignisauslösung mittels RaiseEvent in TürÖffnen() von keiner Methode behandelt wird und ungehört verhallt.


'...\Klassendesign\Ereignisse\Dynamisch.vb

Option Strict On 
Namespace Klassendesign 
  Class Haus 
    Event Alarm(ByVal wo As String) 
    Sub TürÖffnen(ByVal wo As String) 
      RaiseEvent Alarm(wo) 
    End Sub 
    Sub AlarmAnlage(ByVal wo As String) 
      Console.Beep() : Console.WriteLine("{0}türalarm!", wo) 
    End Sub 
  End Class

  Module Dynamisch 
    Sub Test() 
      Dim villa As Haus = New Haus() 
      AddHandler villa.Alarm, AddressOf villa.AlarmAnlage 
      villa.TürÖffnen("Haus") 
      RemoveHandler villa.Alarm, AddressOf villa.AlarmAnlage 
      villa.TürÖffnen("Terasse") 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt, dass der Alarm beim Öffnen der zweiten Tür nicht ausgelöst wurde, da er mittels RemoveHandler abgeschaltet wurde.

Haustüralarm!

Typkompatibilität

Wenn es die Möglichkeit gibt, dass ein Ereignis und ein Delegate formell gleich sind, stellt sich die Frage, ob man sie beliebig untereinander austauschen kann. Dies ist nicht der Fall, da sie unabhängige Datentypen sind, die nur »zufällig« formell gleich sind. Daher stellen die beiden ersten Zeilen des folgenden Codefragments verschiedene Datentypen dar. Da sie nur gleich und nicht identisch sind, wird der Versuch in Test(), ein Delegate-Objekt mit dem Ereignis zu verknüpfen, vom Compiler zurückgewiesen.

Delegate Sub FutterTyp(ByRef no As Short) 
Event Futter(ByRef no As Short) 
Sub Tier(ByRef no As Short) 
End Sub 
Sub Test() 
  AddHandler  Futter, New FutterTyp(AddressOf Tier) 'Fehler!! 
End Sub

Wird das Ereignis mittels einer As-Klausel deklariert, ist nur ein Typ im Spiel, und die folgenden Zeilen werden vom Compiler fehlerfrei übersetzt:

Delegate Sub FutterTyp(ByRef no As Short) 
Event Futter As FutterTyp 
Sub Tier(ByRef no As Short) 
End Sub 
Sub Test() 
  AddHandler  Futter, New FutterTyp(AddressOf Tier) 'OK! 
End Sub

Reihenfolge der Handler

Wie bei der statischen Methodenbindung stellt sich auch hier die Frage der Reihenfolge der Methodenaufrufe bei Auslösung eines Ereignisses. Das folgende Codefragment simuliert eine Fütterung im Tierpark. Wolf und Uhu haben für das Futter eine statische Bindung neben der dynamischen, die in Test() etabliert wird.


'...\Klassendesign\Ereignisse\ReihenfolgeAddHandler.vb

Option Strict On 
Namespace Klassendesign 
  Class Tierpark 
    Delegate Sub Nehmen(ByRef no As Short) 
    Event Futter As Nehmen 
    Event Wasser As Nehmen 
    Sub Fütterung() 
      Console.WriteLine("--Futter--") : RaiseEvent Futter(0) 
      Console.WriteLine("--Wasser--") : RaiseEvent Wasser(0) 
    End Sub

    Sub Wolf(ByRef no As Short) Handles Me.Futter 
      no += 1S : Console.WriteLine("Wolf: {0}", no) 
    End Sub 
    Sub Uhu(ByRef no As Short) Handles Me.Futter 
      no += 1S : Console.WriteLine("Uhu: {0}", no) 
    End Sub 
    Sub Reh(ByRef no As Short) 
      no += 1S : Console.WriteLine("Reh: {0}", no) 
    End Sub

    Shared Sub Test() 
      Dim park As Tierpark = New Tierpark()

      AddHandler park.Futter, AddressOf park.Reh 
      AddHandler park.Futter, AddressOf park.Wolf

      AddHandler park.Wasser, AddressOf park.Reh 
      AddHandler park.Wasser, AddressOf park.Uhu 
      AddHandler park.Wasser, AddressOf park.Reh 
      AddHandler park.Wasser, AddressOf park.Wolf

      park.Fütterung() 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe für das Futter zeigt, dass die dynamisch gebundenen Methoden nach den statischen an die Reihe kommen. Beim Wasser ist zu sehen, dass die Reihenfolge der dynamisch gebundenen Methoden erhalten bleibt – im Gegensatz zu den statisch gebundenen.

--Futter-- 
Uhu: 1 
Wolf: 2 
Reh: 3 
Wolf: 4

--Wasser-- 
Reh: 1 
Uhu: 2 
Reh: 3 
Wolf: 4

Automatisch erzeugter Code

Durch die Definition eines Ereignisses werden vom Compiler einige zusätzliche Klassenmitglieder erzeugt (siehe Abschnitt 3.10.1, »Event und RaiseEvent«).


Hinweis
Das im Ereignis implizit enthaltene Objekt kann nicht vom Garbage Collector aufgeräumt werden. Daher ist eine Deregistrierung von nicht mehr benötigten Ereignissen empfehlenswert.


Diese werden bei der dynamischen Methodenbindung implizit genutzt. Die Deklarationen

AddHandler Ereignis, Delegate 
RemoveHandler Ereignis, Delegate

münden in den folgenden Aufrufen (beide Public):

Public add_Ereignis(Delegate) 
Public remove_Ereignis(Delegate)

Obwohl die Methoden öffentlich sind, weist er Compiler Zugriffsversuche zurück. Da sie Teil der Klasse sind, dürfen Sie nicht gleichnamige Klassenmitglieder definieren. Selbst eine Überladung ist nicht gestattet.


Rheinwerk Computing - Zum Seitenanfang

3.10.4 Benutzerdefinierte Ereignisse: Custom Event Zur nächsten ÜberschriftZur vorigen Überschrift

Es gibt Situationen, in denen Sie mehr Kontrolle über Ereignisse benötigen. Zum Beispiel möchten Sie bei einer Alarmanlage jede Auslösung mitprotokollieren, auch wenn keine Methode zur Behandlung registriert ist und der Alarm »still« ist. Vielleicht möchten Sie auch die Reihenfolge von statisch an ein Ereignis gebundenen Methoden bestimmen. Diese Maßnahmen sollten unabhängig davon sein, wie ein Ereignis verwendet wird, um sicherzustellen, dass sie immer greifen. In der Realität ist es ja auch nicht sinnvoll, wenn eine Alarmanlage nur bei »richtiger« Verwendung durch den Einbrecher anschlägt.

Zur Definition eines benutzerdefinierten Ereignisses wird in Visual Basic die folgende Syntax verwendet (optionale Teile sind in eckige Klammern gesetzt und kursive Teile müssen Sie Ihren Bedürfnissen anpassen):


Custom Event Ereignis As Delegate-Typ 
  AddHandler(ByVal handler As Delegate-Typ) 
    [<Anweisungen>] 
  End AddHandler 
  RemoveHandler(ByVal handler As Delegate-Typ) 
    [<Anweisungen>] 
  End RemoveHandler 
  RaiseEvent(([<Parameter>]) 
    [<Anweisungen>] 
  End RaiseEvent 
End Event

Die Verwendung des Ereignisses ändert sich nicht: Die Operatoren AddHandler, RemoveHandler und RaiseEvent rufen implizit die korrespondierenden Methoden des benutzerdefinierten Ereignisses auf.


Hinweis
Die Methode RaiseEvent muss entweder zum Delegate kompatible Parametertypen oder gar keine Parameter haben. Die Parameter des RaiseEvent-Operators müssen zur RaiseEvent-Methode passen (beide typkompatibel oder beide leer).


Als Beispiel dient die Verwaltung einer Warteschlange, die auf ein Druckereignis wartet. Als Erstes definieren wir dazu einen Druckjob. Er besteht aus einer Priorität und einem Text. Außerdem enthält er eine Methode zum Drucken, die das Druck-Ereignis behandeln wird.


'...\Klassendesign\Ereignisse\Benutzerdefiniert.vb

Option Strict On 
Namespace Klassendesign 
  Class Job 
    Friend ReadOnly prio As Integer 
    Friend ReadOnly text As String 
    Sub New(ByVal prior As Integer, ByVal text As String) 
      Me.prio = prior : Me.text = text 
    End Sub 
    Sub Druck(ByVal no As Integer) 
      Console.WriteLine("Druck {0} mit Prio {1}: {2}", no, prio, text) 
    End Sub 
  End Class 
  ... 
End Namespace

Die Warteschlange fügt einen Druckjob abhängig von seiner Priorität in eine Liste ein. Dies ist mit Standardereignissen nicht möglich, da wir dort keinen Einfluss auf die Reihenfolge nehmen können. Die vielen Typumwandlungen mit CType könnten mit generischen Datentypen vermieden werden (siehe Abschnitt 4.4, »Generisches«). Um nicht vorzugreifen, wurde hier auf ihren Einsatz verzichtet. In RaiseEvent wird die behandelnde Methode nach deren Aufruf mit RemoveHandler aus der Liste entfernt, und der Druckjob wird gelöscht. Auch dies ist bei Standardereignissen nicht möglich. Es wird in der Schleife immer auf das erste Listenelement zugegriffen, um die Reihenfolge der Druckjobs beizubehalten.


'...\Klassendesign\Ereignisse\Benutzerdefiniert.vb

Option Strict On 
Namespace Klassendesign 
  ... 
  Class Warteschlange 
    Delegate Sub Typ(ByVal no As Integer) 
    Dim liste As ArrayList = New ArrayList()

    Custom Event Druck As typ 
      AddHandler(ByVal meth As Typ) 
        Dim prio As Integer = CType(meth.Target, Job).prio 
        For no As Integer = 0 To liste.Count 
          If no = liste.Count Then : liste.Add(meth) : Else 
            Dim pr As Integer = _ 
              CType(CType(liste(no), typ).Target, Job).prio 
            If prio > pr Then 
              liste.Insert(no, meth) : Exit For 
            End If 
          End If 
        Next 
      End AddHandler

      RemoveHandler(ByVal meth As Typ) 
        liste.Remove(meth) 
      End RemoveHandler

      RaiseEvent(ByVal no As Integer) 
        For nr As Integer = 0 To liste.Count – 1 
          CType(liste(0), typ)(no) 
          RemoveHandler Druck, CType(liste(0), typ) 
        Next 
      End RaiseEvent 
    End Event

    Sub drucken(ByVal no As Integer) 
      RaiseEvent Druck(no) 'implizit Druck.RaiseEvent(no) 
    End Sub 
  End Class 
  ... 
End Namespace

Schließlich werden einige Druckjobs mittels AddHandler in die Warteschlange eingefügt und es wird ein Druck-Ereignis mittels der Methode drucken() ausgelöst.


'...\Klassendesign\Ereignisse\Benutzerdefiniert.vb

Option Strict On 
Namespace Klassendesign 
  ... 
  Module Benutzerdefiniert 
    Sub Test() 
      Dim w As Warteschlange = New Warteschlange() 
      'implizit w.Druck.AddHandler(new Job(...)) 
      AddHandler w.Druck, AddressOf (New Job(2,"2-1")).Druck 
      AddHandler w.Druck, AddressOf (New Job(3,"3-1")).Druck 
      AddHandler w.Druck, AddressOf (New Job(1,"1-1")).Druck 
      AddHandler w.Druck, AddressOf (New Job(2,"2-2")).Druck 
      AddHandler w.Druck, AddressOf (New Job(1,"1-2")).Druck 
      w.drucken(7) 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe zeigt die korrekte Sortierung nach Priorität:

Druck 7 mit Prio 3: 3-1 
Druck 7 mit Prio 2: 2-1 
Druck 7 mit Prio 2: 2-2 
Druck 7 mit Prio 1: 1-1 
Druck 7 mit Prio 1: 1-2

Automatisch erzeugter Code

Durch die Deklaration eines benutzerdefinierten Ereignisses werden vom Compiler automatisch folgende Methoden der Klasse hinzugefügt:

Public Sub add_Ereignis(ByVal handler As Delegate-Typ) 
Public Sub remove_Ereignis(ByVal handler As Delegate-Typ) 
Private Sub raise_Ereignis()

Eine Benutzung im eigenen Quelltext gestattet der Compiler nicht, er beschwert sich aber, wenn Sie versuchen, ein gleichnamiges Klassenmitglied zu definieren.


Rheinwerk Computing - Zum Seitenanfang

3.10.5 Umwandlungen Zur nächsten ÜberschriftZur vorigen Überschrift

Da ein Ereignis implizit ein Delegate darstellt, finden auch die gleichen Umwandlungen statt (siehe Abschnitt 3.9.6, »Umwandlungen«, unter anderem kommt es zu impliziten Typkonvertierungen). Auch bei Ereignissen gibt es eine »unglückliche« Stolperfalle, auf die hier besonders hingewiesen wird. Wird ein Ereignis mit Parametern vereinbart, darf die behandelnde Methode ohne Parameter sein. In allen anderen Fällen muss der Aufruf mit korrekter Parameterzahl und Parametertypen erfolgen. Da die Methode keine Parameter hat, werden die übergebenen Parameter schlicht ignoriert. Das folgende Codefragment zeigt eine solche verwirrende Situation:


'...\Klassendesign\Ereignisse\Parameterlos.vb

Option Strict On 
Namespace Klassendesign 
  Class VergesseneParameter 
    Event Ereignis(ByVal no As Integer)

    Sub Auslösung() 
      RaiseEvent Ereignis(-3) 
    End Sub

    Sub Reaktion() Handles Me.Ereignis 
      Console.WriteLine("es geht auch ohne Parameter") 
    End Sub

    Shared Sub Test() 
      Dim ver As VergesseneParameter = New VergesseneParameter() 
      ver.Auslösung() 
      Console.ReadLine() 
    End Sub 
  End Module 
End Namespace

Die Ausgabe bestätigt, dass die behandelnde Methode trotz fehlender Parameter aufgerufen wurde:

es geht auch ohne Parameter

Rheinwerk Computing - Zum Seitenanfang

3.10.6 Grafikbibliothek: Größenänderungen von Rechtecken überwachen topZur vorigen Überschrift

In diesem Abschnitt erweitern wir die in Abschnitt 3.1.12, »Grafikbibliothek: Beispiel für Kapitel 3 und 4«, eingeführte und zuletzt in Abschnitt 3.9.7, »Grafikbibliothek: Vergleich von Rechtecken«, erweiterte Grafikanwendung. Als Erstes wird das Ereignis BreiteÄndern definiert und bei einer Änderung der Eigenschaft Breite über RaiseEvent ausgelöst. Zur statischen Bindung einer Methode an das Ereignis wird ein Rechteck mittels WithEvents als ereignisbehaftet gekennzeichnet, durch den dritten Konstruktorparameter belegt und mit einer Handles-Klausel an die Methode BreiteGeändert() gebunden.


'...\Klassendesign\Graphik\Ereignisse.vb

Option Explicit On 
Namespace Klassendesign 
  Partial Public Class Rechteck

    Event BreiteÄndern As Vergleich

    Property Breite() As Double 
      Get 
        Return b 
      End Get 
      Set(ByVal b As Double) 
        Me.b = b 
        RaiseEvent BreiteÄndern(Me) 
      End Set 
    End Property

    Private WithEvents recht As Rechteck

    Sub New(ByVal a As Double, ByVal b As Double, ByVal recht As Rechteck) 
      Me.New(a, b) 
      Me.recht = recht 
    End Sub

    Sub BreiteGeändert(ByVal recht As Rechteck) Handles recht.BreiteÄndern 
      Vgl(recht, Function(r As Rechteck) r.b, "Breite") 
    End Sub 
  End Class 
End Namespace

Zum Test wird ein Rechteck akteur erzeugt, dessen Breite geändert wird. Als Ereignishandler werden mittels AddHandler die Rechtecke aufmerksam und schläfrig verwendet, wobei aufmerksam durch den übergebenen akteur und die Handles-Klausel des letzten Codefragments einen weiteren (statischen) Ereignishandler bekommt.


'...\Klassendesign\Zeichner\Ereignisse.vb

Option Explicit On 
Namespace Klassendesign 
  Partial Class Zeichner 
    Sub Ereignisse() 
      Dim akteur As New Rechteck(7, 2) 
      Dim aufmerksam As Rechteck = New Rechteck(6, 7, akteur) 
      Dim schläfrig As Rechteck = New Rechteck(12, 4) 
      AddHandler akteur.BreiteÄndern, AddressOf aufmerksam.VglFläche 
      AddHandler akteur.BreiteÄndern, AddressOf schläfrig.VglUmfang 
      akteur.Breite = 7 
    End Sub 
  End Class 
End Namespace

Zur Kontrolle sehen Sie hier die Ausgabe der Methode Ereignisse():

7x7 hat mehr Breite als 6x7 
7x7 hat mehr Fläche als 6x7 
7x7 hat weniger Umfang als 12x4

Die nächste Erweiterung erfolgt in Abschnitt 3.11.6, »Grafikbibliothek: Addition und Umwandlung von Rechtecken«.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual Basic 2008
Visual Basic 2008
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Visual Basic 2012






 Visual Basic 2012


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de