Frage zu eigenen klassenübergreifenden Events

  • VB.NET

Es gibt 30 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Frage zu eigenen klassenübergreifenden Events

    Ist es möglich klassenübergreifende Events zu schaffen, die vom Hauptprogramm oder von einer unergeordneten Klasse empfangen werden können?

    Zum Hintergrund: Ich schreibe an einer Messwerterfassung mit graphischer Auswertung. Die Daten liegen in einem typ. Dataset vor, auf welches ich von überall her zugreifen kann. Eine Klasse ist für die Messung zuständig, in einer anderen Klasse können die Daten verarbeitet/manipuliert werden. Eine dritte Klasse ist für die graphische Darstellung zuständig. Schön wäre es, wenn die ersten beiden Klassen Events auslösen können, wenn das Diagramm aktualisiert werden muss. Der Event müsste dann von der dritten Klasse empfangen werden. Zur Not auch vom Hauptprogramm, welches dann die Aktualisierung des Diagramms auslösen würde.
    Auf das typisierte Dataset kann ich von überall her zugreifen, da alles über ein "Friend Module" läuft.
    Wie löst von solche Probleme? Was hat sich bewährt?
    Du kannst ein statisches (Public Shared) Event programmieren - das kann wo auch immer abonniert werden.
    Shared Events bergen aber in bestimmten Zusammenhängen die Gefahr von MemoryLeaks.
    Weil solange wie abonniert ist kann der GC den Abonnenten nicht aufräumen - erst nach de-abonnierung.

    vlt. gugge Alles über Events

    allerdings sind shared Events so speziell, dass ich sie im Tut nicht behandelt hab (dennoch ist das Tut-KnowHow Vorraussetzung, um auch damit umzugehen).
    @egon Probier dies:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private WithEvents MyTestClass As TestClass
    3. Public Sub New()
    4. InitializeComponent()
    5. Me.MyTestClass = New TestClass
    6. End Sub
    7. Private Sub MyTestEvent(sender As Object, e As EventArgs) Handles MyTestClass.TestEvent
    8. MessageBox.Show("TestEvent")
    9. End Sub
    10. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    11. Me.MyTestClass.Test()
    12. End Sub
    13. End Class
    14. Class TestClass
    15. Public Event TestEvent(sender As Object, e As EventArgs)
    16. Public Sub Test()
    17. RaiseEvent TestEvent(Me, EventArgs.Empty)
    18. End Sub
    19. End Class
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    Mit dem Codeschnipsel habe ich herumgespielt und um eine zweite Klasse erweitert. Soweit funktioniert es. Wenn ich aber nun ein zweites Form einbaue läuft es nicht mehr so rund. Ich habe keine Ahnung ob ich es so richtig mache? Bisher kann ich im zweiten Form nur über den ersten Button einen Event auslösen. Über einen eigenen Event "TestEvent" kann ich in Form2 leider keinen Event auslösen. Wie würdet ihr es dann lösen. Wie würde man in Form2 über eine dort instanzierte Klasse einen Event auslösen?


    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class Form1
    3. Private WithEvents MyTestClass As TestClass
    4. Private WithEvents MyTestClass_Zwei As TestClass_Zwei
    5. Private WithEvents MyForm2 As Form2
    6. Public Handler As EventHandler
    7. Public Sub New()
    8. InitializeComponent()
    9. Me.MyTestClass = New TestClass
    10. Me.MyTestClass_Zwei = New TestClass_Zwei
    11. End Sub
    12. Private Sub MyTestEvent(sender As Object, e As EventArgs) Handles MyTestClass.TestEvent, MyTestClass_Zwei.TestEvent
    13. MessageBox.Show("TestEvent")
    14. End Sub
    15. Private Sub MyTestEventForm2(sender As Object, e As EventArgs)
    16. MessageBox.Show("Form2 Button1")
    17. End Sub
    18. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    19. Me.MyTestClass.Test()
    20. End Sub
    21. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    22. Me.MyTestClass_Zwei.Test()
    23. End Sub
    24. Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    25. Handler = New EventHandler(AddressOf MyTestEventForm2)
    26. Using frm = New Form2(Me.Handler) ' Hier muss es irgenwie anders gemacht werden. Aber wie?
    27. ' Using frm = New Form2
    28. Dim result = frm.ShowDialog
    29. End Using
    30. End Sub
    31. End Class
    32. Class TestClass
    33. Public Event TestEvent(sender As Object, e As EventArgs)
    34. Public Sub Test()
    35. RaiseEvent TestEvent(Me, EventArgs.Empty)
    36. End Sub
    37. End Class
    38. Class TestClass_Zwei
    39. Public Event TestEvent(sender As Object, e As EventArgs)
    40. Public Sub Test()
    41. RaiseEvent TestEvent(Me, EventArgs.Empty)
    42. End Sub
    43. End Class
    44. ' ###############################################################
    45. Public Class Form2
    46. Public Sub New(MyEventHandler As EventHandler)
    47. MyBase.New()
    48. Me.InitializeComponent()
    49. AddHandler Button1.Click, MyEventHandler
    50. End Sub
    51. Public Event TestEvent(sender As Object, e As EventArgs) ' wie kann hier der Event weitergegeben werden?
    52. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    53. ' Event wird ausgelöst
    54. End Sub
    55. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    56. Eventausloesung()
    57. End Sub
    58. Public Sub Eventausloesung()
    59. RaiseEvent TestEvent(Me, EventArgs.Empty)
    60. End Sub
    61. End Class
    Dateien
    • EventTest2.zip

      (23,52 kB, 20 mal heruntergeladen, zuletzt: )

    egon schrieb:

    Höre ich da zwischen den Zeile heraus, dass es für einen Anfänger besser ist, wenn jede Klasse die meinen Daten verändern kann eigene Events auslöst, die dann vom Hauptprogramm empfangen und ausgewertet werden?
    nö. ich meine 2 Sachen.
    1. ein Shared Event würde evtl. ganz genau leisten, was dir vorschwebt
    2. um solch zu basteln muss man halt Events gut verstehen
    3. Events verstehen musst du so oder so iwann mal lernen.

    egon schrieb:

    Wie würde man in Form2 über eine dort instanzierte Klasse einen Event auslösen?
    Alle Klassen, deren Instanzen gesehen werden, können das Event empfangen, ansonsten baust Du Dir eine Aufruf-Kette, die das Ebent zum Ziel führt.
    Bei Dir ist das Problem, dass Form2 erst innerhalb einer anderen Prozedur (nicht Konstruktor) instanziiert wird, da musst Du mit AddHandler rangehen.
    Ich hab mal Deinen Code entsprechend modifiziert:
    Form1.vb
    Form2.vb
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    ErfinderdesRades: >>> Events verstehen musst du so oder so iwann mal lernen.
    Da hast du bestimmt mal wieder recht und von dem programmieren eines statischen Public Shared Event bin ich noch sehr weit entfernt. Ich wäre schon froh, wenn ich ein Beispiel auf mein Programm übertragen könnte. Ein solches Beispiel habe ich auch noch nicht gefunden...
    [EDIT] Hierr habe ich ein Beispiel gefunden und werde es mir genauer ansehen und in mein kleines Testprojekt einbauen - wenn ich es hinbekomme: codeproject.com/Articles/28753…ents-for-your-application

    RodFromGermany: Danke. Wie würdest du das Form2 instanzieren und modal aufrufen?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „egon“ ()

    Dafür gibt es eigentlich ein Design Pattern (GoF), denn du als Lösungsmuster für solche Fälle anwenden kannst.

    Evtl. is etwas kompliziert am Anfang aber wenn du es verstehst, wird dir SEHR helfen.

    Suche mal Observer Pattern.

    Aber im Prinzip du solltest :

    1. Die grafische Sachen werden in der Form Behind-Code behandelt.
    2. Die Form hat 1 referenz (private Variable WithEvents) für jede Klasse von der Events behandelt werden müssen.
    3. Du handelst die Events in der Form.

    Man sollte eigentlich mit Interfaces und nicht mit konkreten Klassen arbeiten, so dass man später besser erweitern kann, aber das ist evtl. eine andere Gesichte.
    Life doesn't give you a datasheet. Sometimes the docs are wrong and you have to try it.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „rgomez“ ()

    @egon

    egon schrieb:

    Wie würdest du das Form2 instanzieren und modal aufrufen?

    Das steht doch in Form1.vb bei Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click schon genau drin, wie er Form2 aufrufen würde. Oder meinst Du was anderes?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    RFG hat geschrieben: "Bei Dir ist das Problem, dass Form2 erst innerhalb einer anderen Prozedur (nicht Konstruktor) instanziiert wird,..."

    Ich wollte an dieser Stelle nur nachfragen, ob es noch andere Wege gibt.

    Bei mir läuft übrigens schon ein globaler Event. Das Beispiel aus codeproject konnte ich übertragen.
    codeproject.com/Articles/28753…ents-for-your-application
    Ich melde mich später mit meiner Umsetzung.
    Was haltet ihr von dieser Variante mit globalen Events?

    Immer wenn ich einen Handler mit AddHandler hinzufüge erscheint der Warnhinweis:
    Warnung BC42025: Zugriff des freigegebenen Members, konstanten Members, Enumerationsmembers oder geschachtelten Typs über eine Instanz; der qualifizierende Ausdruck wird nicht ausgewertet.

    Was kann ich machen um diese Warnung zu verhindern? Wie kann ich den Ausdruck auswerten?


    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class Form1
    3. Private WithEvents MyTestClass As TestClass
    4. Private WithEvents MyTestClass_Zwei As TestClass_Zwei
    5. ' Private WithEvents MyForm2 As Form2
    6. Private MyForm2 As Form2
    7. 'Public Handler As EventHandler
    8. Dim gEvent As GlobalEventing
    9. Public Sub New()
    10. InitializeComponent()
    11. Me.MyTestClass = New TestClass
    12. Me.MyTestClass_Zwei = New TestClass_Zwei
    13. AddHandler gEvent.SpecialEventRaised, AddressOf SpecialEventFired
    14. End Sub
    15. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    16. Me.MyTestClass.Test()
    17. End Sub
    18. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    19. Me.MyTestClass_Zwei.Test()
    20. End Sub
    21. Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    22. Using frm = New Form2()
    23. Dim result = frm.ShowDialog
    24. End Using
    25. End Sub
    26. Private Sub SpecialEventFired(ByVal sender As Object, ByVal typ As String, ByVal msg As String)
    27. MessageBox.Show("globaler Event ausgelöst - Empfangen in Form1" & " " & sender.ToString & " " & typ & " " & msg)
    28. End Sub
    29. End Class
    30. Class TestClass
    31. Dim gEvent As GlobalEventing
    32. Public Sub New()
    33. AddHandler gEvent.SpecialEventRaised, AddressOf SpecialEventFired
    34. End Sub
    35. Private Sub SpecialEventFired(ByVal sender As Object, ByVal typ As String, ByVal msg As String)
    36. MessageBox.Show("globaler Event ausgelöst - Empfangen in TestClass" & " " & sender.ToString & " " & typ & " " & msg)
    37. End Sub
    38. Public Sub Test()
    39. GlobalEventing.SpecialEvent(Me, "my type", "my message")
    40. End Sub
    41. End Class
    42. Class TestClass_Zwei
    43. Dim gEvent As GlobalEventing
    44. Public Sub New()
    45. AddHandler gEvent.SpecialEventRaised, AddressOf SpecialEventFired
    46. End Sub
    47. Private Sub SpecialEventFired(ByVal sender As Object, ByVal typ As String, ByVal msg As String)
    48. MessageBox.Show("globaler Event ausgelöst - Empfangen in TestClass_Zwei" & " " & sender.ToString & " " & typ & " " & msg)
    49. End Sub
    50. Public Sub Test()
    51. GlobalEventing.SpecialEvent(Me, "my type", "my message")
    52. End Sub
    53. End Class

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form2
    2. Dim gEvent As GlobalEventing
    3. Public Event TestEvent(ByVal sender As Object, ByVal e As EventArgs) ' wie kann hier der Event weitergegeben werden?
    4. Private Sub ChildForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    5. AddHandler gEvent.SpecialEventRaised, AddressOf SpecialEventFired
    6. End Sub
    7. Private Sub SpecialEventFired(ByVal sender As Object, ByVal typ As String, ByVal msg As String)
    8. MessageBox.Show("globaler Event ausgelöst - Empfangen in Form2" & " " & sender.ToString & " " & typ.ToString & " " & msg) 'Event Now().ToLocalTime.ToString & " received " & typ & " / " & msg) ' & vbCrLf)
    9. End Sub
    10. Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    11. GlobalEventing.SpecialEvent(Me, "my type", "my message")
    12. End Sub
    13. Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
    14. RemoveHandler gEvent.SpecialEventRaised, AddressOf SpecialEventFired
    15. End Sub
    16. End Class

    Spoiler anzeigen

    VB.NET-Quellcode

    1. '(c) Daniel Ch. Bloch, 8-2008 (http://blog.solutiondev.ch)
    2. ' https://www.codeproject.com/Articles/28753/Global-events-for-your-application
    3. '
    4. 'Public and Shared so that it is reachable anywhere in the application
    5. Public Class GlobalEventing
    6. 'Define Event that can be handled anywhere
    7. 'Define your own parameters as you need
    8. Public Shared Event SpecialEventRaised(ByVal sender As Object, ByVal type As String, ByVal msg As String)
    9. 'Define Procedure that can be called from anywhere
    10. Public Shared Sub SpecialEvent(ByVal sender As Object, ByVal type As String, ByVal msg As String)
    11. RaiseEvent SpecialEventRaised(sender, type, msg)
    12. End Sub
    13. End Class

    Das Projekt habe ich auch als Datei angehängt.
    Dateien
    @egon Die Aussage bezog sich auf die Möglichkeit, das Event per Handles MyForm2.EventX zu lösen.
    Die Instanziierung Deiner Form2 war in Ordnung, nun ist sie es nicht mehr.
    Die Deklaration einer modal aufgerufene Form gehört üblicherweise nicht in die Klasse, sondern in die Prozedur.
    Die Übertragung des Delegaten an einen Dialog ist eine übliche Herangehensweise, allerdings solltest Du Dich dann mal mit Delegates befassen.
    docs.microsoft.com/de-de/dotne…guage-features/delegates/
    Invoke, Delegate, Klasse

    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    Du hast

    VB.NET-Quellcode

    1. Dim gEvent As GlobalEventing
    2. '[...]
    3. AddHandler gEvent.SpecialEventRaised, AddressOf SpecialEventFired
    4. '[...]
    5. Public Class GlobalEventing
    6. '[...]
    7. Public Shared Event SpecialEventRaised(ByVal sender As Object, ByVal type As String, ByVal msg As String)

    Durch das Public Shared Event machst Du klar: Für alle GlobalEventing-Klasseninstanzen steht dieses eine Event zur Verfügung. Nicht jede Instanz hat ihr eigenes, sondern alle verwenden dieses Shared Event. Dadurch ergibt sich, dass Du die AddHandler-Sache nicht mit einer GlobalEventing-Klasseninstanz schreiben musst, sondern es reicht der Name der Klasse:

    VB.NET-Quellcode

    1. AddHandler GlobalEventing.SpecialEventRaised, AddressOf SpecialEventFired
    Eben weil es instanzenunabhängig ist.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    VaporiZed: Das hat mir sofort eingeleuchtet. Ist schon verbessert worden und sieht nun sehr schlüssig für mich aus.

    RodFromGermany: Kannst du mir bitte mitteilen wo etwas nicht in Ordnung ist. Ich kann dir leider nicht folgen.

    ErfinderDesRades: In beitrag #2 hast du geschrieben, dass "Shared Events" in bestimmten Zusammenhängen die Gefahr von MemoryLeaks bergen. Worauf muss ich achten um mir nicht neue Probleme anzulachen?


    Hier habe ich mal den Code auf zwei Events erweitert. Damit hätte ich alle Freiheiten die ich mir vorstellen kann. Entweder werden unterschiedliche Events erschaffen oder es wird über "type" unterschieden.
    In einem richtigen Projekt müssten die Namen natürlich aussagekräftiger werden ;)
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class GlobalEventing
    2. 'Define Event that can be handled anywhere
    3. 'Define your own parameters as you need
    4. Public Shared Event SpecialEventRaised(ByVal sender As Object, ByVal type As String, ByVal msg As String)
    5. Public Shared Event SpecialEventValueRaised(ByVal sender As Object, ByVal type As String, ByVal value As Double)
    6. 'Define Procedure that can be called from anywhere
    7. Public Shared Sub SpecialEvent(ByVal sender As Object, ByVal type As String, ByVal msg As String)
    8. RaiseEvent SpecialEventRaised(sender, type, msg)
    9. End Sub
    10. Public Shared Sub SpecialEventValue(ByVal sender As Object, ByVal type As String, ByVal value As Double)
    11. RaiseEvent SpecialEventValueRaised(sender, type, value)
    12. End Sub
    13. End Class

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „egon“ ()

    egon schrieb:

    wo etwas nicht in Ordnung ist

    egon schrieb:

    VB.NET-Quellcode

    1. ' Private WithEvents MyForm2 As Form2
    2. Private MyForm2 As Form2
    gehört in die Prozedur, nicht in die Klasse.
    Was an Deinem / meinem Code in Post #7 ist unklar?
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    @RodFromGermany: In Post #7 hattet du geschrieben: "Bei Dir ist das Problem, dass Form2 erst innerhalb einer anderen Prozedur (nicht Konstruktor) instanziiert wird, da musst Du mit AddHandler rangehen."
    An dieser Stelle hatten wir das dann mit dem AddHandler gelöst. Hätte man an dieser Stelle Form2 auch anders instanzieren können?

    egon schrieb:

    Hätte man an dieser Stelle Form2 auch anders instanzieren können?
    Können gewiss, aber das wäre suboptimal, da Du die modale Form dann nur ein Mal aufrufen könntest, beim zweiten Mal müsstest Du sie eh neu instanziieren und die Handler wären flöten.
    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).
    VB-Fragen über PN / Konversation werden ignoriert!