Problem mit Multithreading und Textbox

Seberoth

Grünschnabel
Hallo zusammen,

ich möchte gerne aus einem Thread hinaus in meine Mainform änderungen an einer Textbox machen. Der dafür verwendete String kann problemlos per MsgBox angezeigt werden. Wenn ich die änderunegn jedoch in die Textbox schreiben lasse, werden diese nicht angezeigt (gibt aber trotzdem keine fehler). Application.DoEvents() brachte leider auch nicht den gewünschten erfolg.

MainForm.vb:
Code:
Public Class MainForm

    Private WithEvents wsClient As New ChatHandler()
    Private Delegate Sub DAddLine(ByVal msg As String, ByVal target As String)

    Public nick As String = My.Settings.nick
    Public mail As String = My.Settings.mail
    Public uid As String = My.Settings.uid

    Public server As String = My.Settings.server
    Public port As String = My.Settings.port

    Public Sub AddLineMethod(ByVal msg As String, ByVal target As String)
        If Me.InvokeRequired Then
            ' Argument array for the delegate
            Dim args As Object() = {msg, target}
            ' Method to call
            Dim TmpDelegate As DAddLine
            TmpDelegate = AddressOf AddLineMethod
            ' After this you're in the main thread (UI thread)
            Me.Invoke(TmpDelegate, args)
            Exit Sub
        End If

        Select Case target
            Case "channel_1"
                main_v1_chat.AppendText(msg & vbCrLf)
                main_v1_chat.ScrollToCaret()
            Case Else
                main_debug.AppendText(msg & vbCrLf)
                main_debug.ScrollToCaret()
        End Select
    End Sub

    Private Sub ConnectToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ConnectToolStripMenuItem.Click
        wsClient.Connect()
    End Sub
End Class

ChatHandler.vb
Code:
Imports System.Net.Sockets
Imports System.IO

Public Class ChatHandler
    Public Event onConnect()

    Private stream As NetworkStream
    Private streamw As StreamWriter
    Private streamr As StreamReader
    Private client As New TcpClient
    Private t As New Threading.Thread(AddressOf listen)

    Public Sub EventHandler(ByVal buf As String)
        Dim keys As String() = buf.Split(New Char() {":"c})
        If keys.Length = 4 Then
            Select Case keys(2)
                Case "LOGIN"
                    MainForm.AddLineMethod(buf, "debug")
                Case Else
                    MainForm.AddLineMethod(buf, "debug")
            End Select
        Else
            MainForm.AddLineMethod(buf, "debug")
        End If
    End Sub

    Public Sub Connect()
        Try
            client.Connect(MainForm.server, MainForm.port)
            If client.Connected Then
                stream = client.GetStream
                streamw = New StreamWriter(stream)
                streamr = New StreamReader(stream)

                streamw.WriteLine(send("LOGIN", {MainForm.nick, MainForm.mail, MainForm.uid}, ""))
                streamw.Flush()

                t.Start()
            Else
                cnc("Verbindung zum Server nicht möglich!")
                Application.Exit()
            End If
        Catch ex As Exception
            cnc("Verbindung zum Server nicht möglich!")
            Application.Exit()
        End Try
    End Sub

    Public Sub DisConnect()
        streamr.Close()
        streamw.Close()
        stream.Close()
        t.Abort()
    End Sub

    Private Sub cnc(ByVal msg As String)
        MainForm.main_info_stat.Text = msg
    End Sub

    Public Sub listen()
        While client.Connected
            Try
                EventHandler(streamr.ReadLine)
            Catch
                cnc("Verbindung zum Server nicht möglich!")
                Application.Exit()
            End Try
        End While
    End Sub

    Public Function send(ByVal param1 As String, ByVal param2 As Array, ByVal param3 As String)
        Dim Count As Integer
        Dim msg As String

        Count = 0
        msg = param1 & " "
        If Not IsNothing(param2) Then
            While Count < param2.Length
                msg &= """" & param2(Count) & """ "
                Count = Count + 1
            End While
        End If
        msg &= ":"
        msg &= msg & param3
        msg &= vbCr & vbLf
        Return msg
    End Function
End Class

Ich hoffe mir kann jemand helfen, damit ich schnell weiter basteln kann :)

MfG,
Seberoth
 

Nico Graichen

Erfahrenes Mitglied
Hi

Hast du im Debugger schon mal geprüft, ob die Methode zwei mal aufgerufen wird und beim zweiten Mal "InvokeRequired" auf False ist?

Zu dem, ob die Informationen auch mit übergeben werden?

Du solltest Step by Step debuggen und schauen, ob die Werte noch korrekt sind und korrekt gesetzt werden
 

Seberoth

Grünschnabel
Ja habe ich bereits getestet. Wenn ich eine MsgBox dazwischen Schreib und mir msg ausgeben lasse, kommt das richtige. Nur die TextBox funktioniert nicht (also das bearbeiten der Form).
 

Nico Graichen

Erfahrenes Mitglied
Wozwischen schiebst du denn die MessageBox?
Hast du via Debugger mal geprüft, was in TextBox.Text steht nachdem du AppendText() aufgerufen hast?
 

Seberoth

Grünschnabel
Die MsgBox hatte ich wie folgt eingefügt:

Code:
        Select Case target
            Case "channel_1"
                MsgBox(msg)
                main_v1_chat.AppendText(msg & vbCrLf)
                main_v1_chat.ScrollToCaret()
            Case Else
                main_debug.AppendText(msg & vbCrLf)
                main_debug.ScrollToCaret()
        End Select

Wenn ich mir die den Inhalt von main_v1_chat.Text ausgeben lasse, wird auch alles angezeigt. Also wird die Form wohl nicht "neu gemalt". Wie gesagt Application.DoEvents() brachte keine erfolg :(
 

Spyke

Premium-User
Ist main_v1_chat sichtbar oder wird es ev. von main_debug überlappt, ev. auch Bug im Designer und der Parent ging verloren (kam bei mir schonmal vor), ...
 

Seberoth

Grünschnabel
Ne. Ist alles noch da. Ich habe grade mal einen Buttun erstellt, der unabhängig von dem Thread einen Text hinzufügt. Dieses funktioniert auch einwasfrei während er läuft. Der Text aus dem Thread erscheint weiterhin nicht.
 

Nico Graichen

Erfahrenes Mitglied
Ist jetzt nur ein Versuch, aber probier mal Control.Invalidate() um das Neuzeichnen zu erzwingen.
Application.DoEvents() gibt der Anwendung nur die Möglichkeit die "angestauten" Events abzuarbeiten, erzingt aber nicht zwingend Neuzeichnen
 

Nico Graichen

Erfahrenes Mitglied
So, Thema ist wieder offen ^^

@PN --> Me.InvokeRequired
Probier mal, auf das InvokeRequired des Controls zu prüfen, nicht von der Form. Obwohl eigentlich die Form auch diese Info haben sollte :confused:
 

Seberoth

Grünschnabel
Entschuldigung für die späte Antwort. Hab die Lösung heut morgen gefunden:

Code:
...
Public Sub AddLineMethod(ByVal msg As String, ByVal target As String)
        Dim main As MainForm = CType(Application.OpenForms(0), MainForm) '<---
        If main.InvokeRequired Then
            ' Argument array for the delegate
            Dim args As Object() = {msg, target}
            ' Method to call
            Dim TmpDelegate As DAddLine
            TmpDelegate = AddressOf AddLineMethod
            ' After this you're in the main thread (UI thread)
            main.Invoke(TmpDelegate, args)
            Exit Sub
        End If
 
        Select Case target
            Case "channel_1"
                main.main_v1_chat.AppendText(msg & vbCrLf)
                main.main_v1_chat.ScrollToCaret()
            Case Else
                main.main_debug.AppendText(msg & vbCrLf)
                main.main_debug.ScrollToCaret()
        End Select
    End Sub
...

Ich kann zwar aus mangelder Erfahrung nicht genau sagen, warum diese Zeile hilft, aber es funktionier problemlos. Vielleicht kannst du es mit ja abschließend noch erklären. Auf jedenfall vielen dank für eure Hilfe :)

MfG,
Seberoth