Eine Frage zum Verständnis von Events.

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Eine Frage zum Verständnis von Events.

    Mit den Events stehe ich echt auf Kriegsfuß.
    Eine Frage zum Verständnis.
    Ich habe 2 Klassen.
    In Klasse A ist ein

    VB.NET-Quellcode

    1. Friend Event EinEventInKlasseA()
    angelegt, dass mit

    VB.NET-Quellcode

    1. RaiseEvent EinEventInKlasseA()
    hin und wieder ausgelöst wird.
    In Klasse B ist dieses Event mit

    VB.NET-Quellcode

    1. AddHandler KlasseA.EinEventInKlasseA, AddressOf TuWas()
    abonniert.
    Trotzdem reagiert Klasse B nicht auf dieses Event in Klasse A.
    Gehe ich nun den Umweg über ein Modul, d.h. Klasse A ruft eine Sub in einem Modul auf und dort wird das Event ausgelöst, dann funktioniert es.
    Liegt das daran, dass ich in Klasse B zwar das Event als solches abonniere, aber damit nicht klar ist, welche Instanz der Klasse A (schließlich könnten es ja mehrere sein) dieses Event auslöst?
    Hallo,

    Kurze Frage: Setzt du AddHandler immer neu, wenn du KlasseA oder KlasseB neu instanziierst? AddHandler fügt den Handler nur in die die entsprechende Instanz einer Klasse ein. Nicht generell in alle Instanzen. Meint: Sobald KlasseA = New ClassA aufgerufen wird reagiert KlasseA nicht mehr auf Event von KlasseB

    Um den Fehler zu finden wäre ein wenig Produktivcode hilfreich

    Hier mal ein Beispiel das Funktionieren dürfte:

    ExampleClass

    VB.NET-Quellcode

    1. Public Class ExampleClass
    2. Public Event IsModified(ModifiedClass As ExampleClass, PropertyName As String)
    3. Private _Vorname As String
    4. Public Property Vorname As String
    5. Get
    6. Return _Vorname
    7. End Get
    8. Set(ByVal value As String)
    9. If Not String.Equals(_Vorname, value) Then
    10. _Vorname = value
    11. RaiseEvent IsModified(Me, "Vorname")
    12. End If
    13. End Set
    14. End Property
    15. Private _Nachname As String
    16. Public Property Nachname As String
    17. Get
    18. Return _Nachname
    19. End Get
    20. Set(ByVal value As String)
    21. If Not String.Equals(_Nachname, value) Then
    22. _Nachname = value
    23. RaiseEvent IsModified(Me, "Nachname")
    24. End If
    25. End Set
    26. End Property
    27. Private _Geburtsort As String
    28. Public Property Geburtsort As String
    29. Get
    30. Return _Geburtsort
    31. End Get
    32. Set(ByVal value As String)
    33. If Not String.Equals(_Geburtsort, value) Then
    34. _Geburtsort = value
    35. RaiseEvent IsModified(Me, "Geburtsort")
    36. End If
    37. End Set
    38. End Property
    39. Private _Geburtsdatum As Date
    40. Public Property Geburtsdatum As Date
    41. Get
    42. Return _Geburtsdatum
    43. End Get
    44. Set(ByVal value As Date)
    45. If Not Date.Equals(_Geburtsdatum, value) Then
    46. _Geburtsdatum = value
    47. RaiseEvent IsModified(Me, "Geburtsdatum")
    48. End If
    49. End Set
    50. End Property
    51. End Class



    ExampleForm

    VB.NET-Quellcode

    1. Public Class ExampleForm
    2. Public Property Persons As List(Of ExampleClass)
    3. Public Property SelectedPerson As ExampleClass
    4. Private Sub MyBase_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
    5. Dim Person1 As New ExampleClass With {.Vorname = "Max", .Nachname = "Mustermann", .Geburtsdatum = New Date(2000, 1, 1), .Geburtsort = "Berlin"}
    6. Dim Person2 As New ExampleClass With {.Vorname = "Sabine", .Nachname = "Testperson", .Geburtsdatum = New Date(2000, 1, 1), .Geburtsort = "Hamburg"}
    7. Dim Person3 As New ExampleClass With {.Vorname = "Yanbel", .Nachname = String.Empty, .Geburtsdatum = New Date(1991, 8, 15), .Geburtsort = String.Empty}
    8. Persons = New List(Of ExampleClass) From {Person1, Person2, Person3}
    9. For Each Person As ExampleClass In Persons
    10. AddHandler Person.IsModified, AddressOf WriteLog
    11. Next
    12. FormatDatagrid()
    13. DgvPersons.DataSource = Persons
    14. End Sub
    15. Private Sub WriteLog(ModifiedData As ExampleClass, ModifiedProperty As String)
    16. Using xStream As New IO.StreamWriter(String.Concat("C:\Temp\Log_", ModifiedData.Vorname, "_", ModifiedData.Nachname, ".txt"), True)
    17. xStream.WriteLine(String.Concat(ModifiedProperty, " geändert"))
    18. End Using
    19. End Sub
    20. Private Sub FormatDatagrid()
    21. DgvPersons.Columns.Clear()
    22. DgvPersons.DataSource = Nothing
    23. DgvPersons.AutoGenerateColumns = False
    24. DgvPersons.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None
    25. DgvPersons.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
    26. DgvPersons.AllowUserToResizeColumns = True
    27. DgvPersons.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing
    28. DgvPersons.AllowUserToAddRows = False
    29. DgvPersons.AllowUserToDeleteRows = False
    30. DgvPersons.SelectionMode = DataGridViewSelectionMode.FullRowSelect
    31. DgvPersons.MultiSelect = False
    32. DgvPersons.Columns.Add(New DataGridViewTextBoxColumn With {.Name = "Vorname", .DataPropertyName = "Vorname", .HeaderText = "Vorname", .[ReadOnly] = True})
    33. DgvPersons.Columns.Add(New DataGridViewTextBoxColumn With {.Name = "Nachname", .DataPropertyName = "Nachname", .HeaderText = "Nachname", .[ReadOnly] = True})
    34. DgvPersons.Columns.Add(New DataGridViewTextBoxColumn With {.Name = "Geburtsdatum", .DataPropertyName = "Geburtsdatum", .HeaderText = "Geburtsdatum", .[ReadOnly] = True, .DefaultCellStyle = New DataGridViewCellStyle With {.Format = "dd.MM.yyyy", .NullValue = String.Empty}})
    35. DgvPersons.Columns.Add(New DataGridViewTextBoxColumn With {.Name = "Geburtsort", .DataPropertyName = "Geburtsort", .[ReadOnly] = True, .HeaderText = "Geburtsort"})
    36. End Sub
    37. Private Sub DgvPersons_SelectionChanged(sender As Object, e As EventArgs) Handles DgvPersons.SelectionChanged
    38. If DgvPersons.SelectedRows IsNot Nothing AndAlso DgvPersons.SelectedRows.Count > 0 Then
    39. SelectedPerson = TryCast(DgvPersons.SelectedRows(0).DataBoundItem, ExampleClass)
    40. txtVorname.DataBindings.Clear()
    41. txtNachname.DataBindings.Clear()
    42. DtpGeburtsdatum.DataBindings.Clear()
    43. txtGeburtsort.DataBindings.Clear()
    44. txtVorname.DataBindings.Add("Text", SelectedPerson, "Vorname")
    45. txtNachname.DataBindings.Add("Text", SelectedPerson, "Nachname")
    46. DtpGeburtsdatum.DataBindings.Add("Value", SelectedPerson, "Geburtsdatum")
    47. txtGeburtsort.DataBindings.Add("Text", SelectedPerson, "Geburtsort")
    48. End If
    49. End Sub
    50. End Class





    Ein Computer wird das tun, was du programmierst - nicht das, was du willst.

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „Yanbel“ ()

    Danke für die Antwort.

    Ja, AddHandler wird jeweils neu instanziiert.
    Ich glaube bei mir ist’s auch eine Frage der Reihenfolge. Diese ist wie folgt:
    Ich starte das Programm, dabei wird zunächst automatisch ein Form der Klasse B geöffnet

    VB.NET-Quellcode

    1. Friend Class frm_KlasseB
    2. Dim i as integer = 0
    3. Private Sub LoadThisForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
    4. AddHandler frm_KlasseA.EinEventInKlasseA, AddressOf TuWas
    5. End Sub
    6. Private Sub TuWas
    7. 'hier soll was passieren, z.B. alle 100ms einen Wert erhöhen
    8. i = i + 1
    9. End Sub
    10. End Class

    Erst im Anschluss wird ggf. ein Form der Klasse A vom Benutzer manuell zusätzlich geöffnet

    VB.NET-Quellcode

    1. Friend Class frm_KlasseA
    2. Friend Event EinEventInKlasseA()
    3. 'Timer Tick ist 100ms
    4. Private Sub Timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer.Tick
    5. RaiseEvent EinEventInKlasseA()
    6. End Sub
    7. End Class

    Dadurch soll Klasse B ein Event von KlasseA abonnieren, von der es aber noch gar keine Instanz gibt.
    AddHandler ist eigentlich auch nicht nötig und könnte ein WithEvents sein, da das Ereignis in Klasse B immer und in jedem Fall eine Reaktion in Klasse A auslösen soll.

    roepke schrieb:

    VB.NET-Quellcode

    1. AddHandler KlasseA.EinEventInKlasseA, AddressOf TuWas()
    Ist sichergestellt, dass zum Zeitpunkt des Aufrufs dieser Zeile die Instanz von KlasseA bereits erstellt ist?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany
    Nein eben nicht. Ich nehme an, dass ist auch das Problem.
    Erst wird Klasse B erstellt und dann ggf. Klasse A.
    Ich kann wohl nichts abonieren, was noch nicht existiert.
    Daher gehe ich gerade den Umweg über ein Modul.
    Klasse A ruft eine Sub im Modul auf, das Modul löst das Event aus und Klasse B reagiert.
    Das geht wohl aber nur mittels AddHandler in Klasse B und nicht mit WithEvent?
    Soll heißen WithEvent in einer Klasseninstanz kann sich nicht auf ein Event in einem Modul beziehen.
    Mir scheint, den Kern - bzw. das Thema - des Problems hast du erfasst.

    roepke schrieb:

    VB.NET-Quellcode

    1. AddHandler KlasseA.EinEventInKlasseA, AddressOf TuWas()
    Das ist schon missverständlich formuliert.
    Wie's aussieht ist KlasseA ein Datentyp, und kein Objekt.
    ein Datentyp hat aber normalerweise kein Event - daher ist die Zeile vmtl. logisch falsch.
    Ein Standard-Fehler, den Vb.Net-anfänger machen ist, dass sie ein Form erstellen (also einen Datentyp, der von Form erbt, zB Form1), und dann mit Form1.Blabla irgendwas damit tun.
    Was logischer Mist ist, denn mit einem Datentyp kann man nix tun, man kann nur mit Objekten (also Instanzen eines Datentyps) was tun.
    Leider ist das in VB.Net aber doch möglich - nur für Forms.
    Wodurch Anfänger den Unterschied zwischen Datentyp und Objekt natürlich nie kapieren - und dann eben sowas hinschreiben

    VB.NET-Quellcode

    1. AddHandler KlasseA.EinEventInKlasseA, AddressOf TuWas()
    Wie gesagt, wenn KlasseA ein Form ist, meckert der Compiler das auch nicht an, nur passiert da nicht unbedingt, was du dir vorstellst.

    Eine andere Möglichkeit, warum der Compiler den Unfug nicht bemeckert, wäre, du hast Option Strict Off
    Dann unbedingt: Visual Studio - Empfohlene Einstellungen!

    Aber nochmal zu Post#5

    roepke schrieb:

    Ich kann wohl nichts abonieren, was noch nicht existiert.
    sehr richtig.

    roepke schrieb:

    Klasse A ruft eine Sub im Modul auf, das Modul löst das Event aus und Klasse B reagiert.
    Das kann dann aber nicht das Event KlasseA.TuWas() sein, weil ein Event kann nur aus seiner eigenen Klasse heraus ausgelöst werden.

    roepke schrieb:

    WithEvent in einer Klasseninstanz kann sich nicht auf ein Event in einem Modul beziehen.
    einigermassen richtig - ganz richtig ist:
    WithEvent kann sich nicht auf ein Event in einem Modul beziehen. (also es ist egal, wo das WithEvents-Objekt deklariert ist.
    Und "in einer Klasseninstanz" ist eiglich auch ein ungültiger Terminus - gemeint ist wohl "in einer Klasse". Weisst ja: KlassenInstanz (=Objekt) und Klasse (=Datentyp) sind verschiedenerlei)

    Also im Modul kann ein Event existieren, und auch eine Methode, die es auslöst. KlasseA kann die Methode aufrufen, und so das Modul-Event auslösen.
    KlasseB kann das ModulEvent abonnieren, aber nur mit AddHandler, weil WithEvents kann nur ein Objekt sein, und von einem Modul kannst du keine Klassen-Instanz erstellen (ein Modul ist kein Datentyp).
    Vielen Dank an ErfinderDesRades, jetzt ging mir ein Licht auf.
    Jetzt hat es auch im Hinblick auf Datentypen und Objekte "klick" gemacht.
    Natürlich sind die Forms Instanzen der Typen, das war auch genau mein Fehler.
    Zusätzlich zum Versuch etwas zu abonnieren, was noch gar nicht existiert.
    Option Strict ist bei mir IMMER ON und ich bin stets bemüht mich an die Empfohlenen Einstellungen zu halten.
    Soweit alles richtig gemacht.
    Und je mehr ich über das Thema Events lese und ausprobiere desto klarer wird es für mich.

    roepke schrieb:

    Daher gehe ich gerade den Umweg über ein Modul.
    Wenn Du im Modul auf GUI-Controls zugreifen willst - lösch diese Prozeduren bzw. das Modul aus Deinem Programm, die machen nur Ärger.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Noch eine Frage zum Verständnis.
    Meine Anwendung besteht aus einem frm_Main und einem frm_Sub.
    Startet die Anwendung wird das frm_Main als Startformular instanziiert.
    Das frm_Sub soll später eine Funktion im Objekt frm_Main aufrufen.
    Wie mache ich das richtig? Wenn ich

    VB.NET-Quellcode

    1. frm_Main.RufeDieseFunktionAuf()

    verwende rufe ich doch nicht das Objekt, sondern den Datentyp auf.
    Realisiere ich das mit einem WithEvent?
    Du übergibst bei der Erstellung von frm_Sub im Konstruktor die frm_Main und legst das dann in einer privaten Variablen in frm_Sub ab, sodass du dann die frm_Main jederzeit aufrufen kannst.

    Nebnebei sollten die Namen von Klassen (Dazu zählen auch Controls und Formulare) generell mit einem Großbuchstaben beginnen.

    roepke schrieb:

    Wenn ich
    frm_Main.RufeDieseFunktionAuf()
    verwende rufe ich doch nicht das Objekt, sondern den Datentyp auf.
    Realisiere ich das mit einem WithEvent?

    Nein, das ist nicht der Datentyp, sondern was anderes. Was genau und wie Du das vermeiden kannst, steht in Warum »Form1.Show« und Co. einem irgendwann ins Bein schießen
    Bzgl. Events: Ja, das untergeordnete Form frm_Sub ruft zum passenden Zeitpunkt (= wenn Du es willst) RaiseEvent DeinEvent auf. Wenn frm_Main sich dafür interessiert, kommt es beim festgelegten EventHandler in frm_Main an.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    roepke schrieb:

    Das frm_Sub soll später eine Funktion im Objekt frm_Main aufrufen.
    Dann kann auch Form2 an Form1 ein Event senden: Dialoge: Instanziierung von Forms und Aufruf von Dialogen
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!