Event wird nicht ausgelöst

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

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

    Event wird nicht ausgelöst

    Ich erstelle eine Anwendung die verschiedene Chatdienste verbinden kann. Dabei repräsentieren die ...Task Klasse die einzelnen Verbindungen (je in einer Liste gespeichert) und empfangen über AddMessage sowohl externe Nachrichten sowie interne Nachrichten. Ist eine Nachricht intern soll ein Event gefeuert werden. Die internen nachrichten kommen aus der ... Bridge klasse, die sich untereinander sehr unterscheiden, in diesem Beispiel ist die TaskBridge ein OffScreen Browser in einem eigenem Thread.

    So wird zum Beispiel eine externe Nachricht gesndet: SGS.Tawk.SendToTawk(p.send, p) Von Intern aus der ...Bridge kommend: parent.AddMesssage(CreateProtocolFromMessage(parent.sender, "Es konnte keine Verbindung hergestellt werden."), True)

    Das Problem ist, dass das Event in AddMessage nicht weitergeleitet wird, RasieEvent wird aber ausgeführt.

    Hier die TawkTask Klasse:

    VB.NET-Quellcode

    1. Namespace SGS.Tawk
    2. Public Class TawkTask
    3. Public bridgeThread As System.Threading.Thread
    4. Public sender As String
    5. Public tawkbridge As TawkBridge
    6. Public messageStack As New List(Of Protocoll.SylGa)
    7. Public Delegate Sub NewMessagemEventHandler(sender As Object, e As NewMessageEventArgs)
    8. Public Event NewMessage As NewMessagemEventHandler
    9. Friend Sub AddMesssage(ByVal e As Protocoll.SylGa, Optional ByVal internal As Boolean = False)
    10. If internal Then
    11. OnNewMessage(New NewMessageEventArgs(e))
    12. Else
    13. SyncLock messageStack
    14. messageStack.Add(e)
    15. End SyncLock
    16. End If
    17. End Sub
    18. Protected Overridable Sub OnNewMessage(e As NewMessageEventArgs)
    19. RaiseEvent NewMessage(Me, e)
    20. End Sub
    21. Sub New(ByVal user As String, ByVal firstMessage As Protocoll.SylGa)
    22. AddMesssage(firstMessage)
    23. sender = user
    24. tawkbridge = New TawkBridge(Me)
    25. bridgeThread = New System.Threading.Thread(AddressOf tawkbridge.MainLoop)
    26. bridgeThread.Start()
    27. End Sub
    28. End Class
    29. End Namespace


    Der TawkTask wird wie folgt erstellt:

    VB.NET-Quellcode

    1. Namespace SGS.Tawk
    2. Module TawkAction
    3. Public tawkThreads As New List(Of TawkTask)
    4. Sub SendToTawk(ByVal user As String, ByVal firstMessage As Protocoll.SylGa)
    5. Dim t As TawkTask = findTawk(user)
    6. If t Is Nothing Then
    7. t = New TawkTask(user, firstMessage)
    8. AddHandler t.NewMessage, AddressOf SGS.main.MessageDispatcher
    9. tawkThreads.Add(t)
    10. End If
    11. End Sub
    12. Function findTawk(ByVal user As String) As Tawk.TawkTask
    13. For Each tawk As TawkTask In tawkThreads
    14. If tawk.sender = user Then
    15. Return tawk
    16. End If
    17. Next
    18. Return Nothing
    19. End Function
    20. End Module
    21. End Namespace


    zu guter letzt das main Modul:

    VB.NET-Quellcode

    1. Namespace SGS
    2. Module main
    3. [...]
    4. Public Sub MessageDispatcher(ByVal sender As Object, ByVal e As Tawk.NewMessageEventArgs)
    5. mainForm.TextBox1.AppendText(e.NewMessage.cmd.ToString)
    6. End Sub
    7. End Module
    8. End Namespace


    Da die Aufrufe intern alle Funktionieren muss irgendwo bei der Bindung des Events ein Problem auftauchen. Aber eigentlich sollte AddHandler t.NewMessage, AddressOf SGS.main.MessageDispatcher funktionieren. Aber Tipps wäre ich sehr erfreut!
    Danke für den Hinweis, jedoch verstehe ich das ehrlich gesagt nicht ganz: main ist ja nur ein Modul und keine Klasse, ich hatte das der Übersicht wegen getrennt. Aber ehrlich gesagt ich komme eigentlich von anderen Programmiersprachen, daher bin ich mir jetzt nicht mehr ganz sicher. So wie cih das verstanden habe dienen Module nur dazu Quellcode aufzuteilen. Oder ist das falsch?
    kann man so sehen.
    Aber wichtiger ist, den Blick zu lenken, wozu Klassen dienen: Deren Code ist nämlich im Unterschied zu Modulen nicht allgemein verfügbar, sondern Klassenmember-Code (es gibt auch in Klassen allgemeinverfügbaren, aber wenig) ist nur für Objekte dieser Klasse verfügbar.



    Aber ich denke dein Problem heisst "Threading und die DefaultForm-Instanz".
    Betrachte

    VB.NET-Quellcode

    1. mainForm.TextBox1.AppendText(e.NewMessage.cmd.ToString)
    - was ist mainForm? Du denkst, es sei ein Objekt, und da sei eine Textbox drauf - das ist aber von der Sprache her falsch.
    mainForm ist zunächstmal ein Datentyp.
    Gehe in den Code von mainForm, und überzeuge dich, dass da steht:

    VB.NET-Quellcode

    1. Public Class mainForm
    2. '
    Ein Datentyp ist aber kein Objekt, sondern ist nur der Plan eines Objektes.
    Und wie du in einem StadtPlan keine Demonstration abhalten kannst (sondern nur in einem wirklichen Stadt-Objekt) - so kannst du in einem Datentyp auch keinen Text anfügen.

    Das große Problem mit vb.net ist: Es geht doch.
    Vb.Net erzeugt im Hintergrund zu jedem Form-Datentyp ein Default-Form-Objekt, was auch noch genau so heisst wie der Datentyp. Das macht es nur für Form-Datentypen - es ist zum Haare ausrupfen.
    Jedenfalls bei Threading gehts dann gründlich schief, weil da entsteht in jedem Thread ein eigenes Default-Form. Haste 5 Threads, die auf obige Weise jeweils ihr mainForm addressieren, dann haste auch 5 mainForm-Objekte.
    Am Bildschirm zu sehen bekommste aber nur eines - weil um ein Form zu sehen muss man frm.Show() aufrufen.

    Es gibt mehrere Lösungen - ich würd vorschlagen, nee - ich schlag erstmal quick & dirty vor:

    VB.NET-Quellcode

    1. Public Sub MessageDispatcher(ByVal sender As Object, ByVal e As Tawk.NewMessageEventArgs)
    2. dim closure as Action = Sub() mainForm.TextBox1.AppendText(e.NewMessage.cmd.ToString)
    3. Application.OpenForms(0).BeginInvoke(closure)
    4. End Sub
    Ist ungetestet - bin ich ja gespannt...
    Kannst auch nach Control.BeginInvoke suchen hier im Forum. Aber nicht verwechseln mit Delegate.BeginInvoke !

    ErfinderDesRades schrieb:

    Das große Problem mit vb.net ist: Es geht doch.
    Vb.Net erzeugt im Hintergrund zu jedem Form-Datentyp ein Default-Form-Objekt, was auch noch genau so heisst wie der Datentyp. Das macht es nur für Form-Datentypen - es ist zum Haare ausrupfen.
    Jedenfalls bei Threading gehts dann gründlich schief, weil da entsteht in jedem Thread ein eigenes Default-Form. Haste 5 Threads, die auf obige Weise jeweils ihr mainForm addressieren, dann haste auch 5 mainForm-Objekte.



    Das erklärt einiges - Ich hab mich nämlich gewundert wieso man auf eine Form so "einfach" zugreifen kann, ich vermutete das irgendwo ein magisches mainForm = New mainForm() steht und hatte schon überlegt wie vb das wohl mit den "doppeltem" Namen löst. Aber nun hab ich's verstanden.

    Zudem ist MainForm offensichtlich auch noch eine Eigenschaft von MyApplication, daher habe ich mainForm kurzer Hand in gui umbenannt, nicht das es da auch noch irgendwelche Probleme gibt.

    Das mir mainForm.TextBox1.AppendText(e.NewMessage.cmd.ToString) um die Ohren fliegen würde, war mir beim schreiben schon klar, ich erwartete an dieser Stelle den freundlichen "Cross-thread operation not valid" Hinweiß des Compilers. Allerdings kam es nie dazu.

    Das Hauptproblem ist, dass MessageDispatcher() nie aufgerufen wird. (Hab da inzwischen auch einen Haltepunkt drin) Ich verstehe nur nicht warum. Denn Raise Event wird ausgeführt: