Click here to Skip to main content
15,891,513 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hej

I am quit new in programming but enjoy doing it on a hobby scale, my question is how to raise an event in the main thread from an other thread?
From the thread i want to use Sub WriteEvent, witch i would like to trigger a event, on the main form, i get the error cross threading.

Code below

Thanks in advance

VB
Imports System.IO
Imports System.Net
Imports System.Threading

Public Class frmMain
    Private _tdatabase As Thread

    Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        AddHandler EventlogChange, AddressOf Eventlog_changed

        _tdatabase = New Thread(AddressOf Me.Database)

        _tdatabase.IsBackground = True
        _tdatabase.Start()
        _tdatabase.Join()
    End Sub

    Private Sub Eventlog_changed(ByVal sender As Object, ByVal e As EventLogArgs)
        TextBox1.Text = e.GetEntries
    End Sub

    Private Sub Database()
        WriteEntry("Test")
    End Sub

#Region "EventLog"
    Private Event EventlogChange As EventHandler(Of EventLogArgs)

    Sub WriteEntry(ByVal description As String)
        If (InvokeRequired) Then

        Else
            'Declare objects.
            Dim _stream As StreamWriter

            'Execute code in try statement.
            Try
                'Create new instance of streamwriter.
                _stream = New StreamWriter(My.Computer.FileSystem.CurrentDirectory.ToString & "\Event.log", True)

                'Write data to streamer.
                _stream.WriteLine(String.Format("{0},{1};", DateTime.Now.ToString("dd'-'MM'-'yyyy' 'hh':'mm':'ss':'fff"), description.ToString))

                'Flush streamer, clear stream buffer.
                _stream.Flush()

                'Close streamer, release all resources.
                _stream.Close()
            Catch ex As Exception
                'On exception, below code will be executed.

                'Throw exception.
                Throw ex

                'Notify user.
                MsgBox("Unable to read eventlog.", MsgBoxStyle.Exclamation, "Eventlog")
            Finally
                'When try finished, below code will be executed.

                'Raise eventlog change event.
                RaiseEvent EventlogChange(New String() {"Eventlog"}, New EventLogArgs)
            End Try
        End If
    End Sub

    Public Class EventLogArgs
        Inherits System.EventArgs

        Public Function GetEntries() As String
            'Declare objects.
            Dim _stream As StreamReader
            Dim _data As String

            'Execute code in try statement.
            Try
                'Create new instance of streamreader
                _stream = New StreamReader(My.Computer.FileSystem.CurrentDirectory.ToString & "\Event.log")

                'Fill string object whit data readed from streamer.
                _data = _stream.ReadToEnd()

                'Close streamer, release all resources.
                _stream.Close()

                'Return string object readed from streamer.
                Return _data
            Catch ex As Exception
                'On exception, below code will be executed.

                'Throw exception.
                Throw ex

                'Notify user.
                MsgBox("Unable to read eventlog.", MsgBoxStyle.Exclamation, "Eventlog")

                'Return noting.
                Return Nothing
            End Try
        End Function
    End Class
#End Region
End Class
Posted

1 solution

Well, one thing to consider might be that calling directly into your form's code from somewhere else might not be the best overall program structure here. You might think about instead creating a class that does your event log writing and raises its own events, then have the form own an object of that type and subscribe to its events.

However, while that might provide a cleaner separation of concerns, you still would need to solve the problem you have here. It isn't the event call itself that causes the trouble, it is that updating a winforms control (as you do inside the event handler) is not a thread-safe operation. A control can only be updated safely from within the thread that created and owns the control. Luckily, there is a way provided to ask the form's control thread to do the work of calling a specified method itself, using the Invoke method.

I believe what you want is something like this in the form's code:
VB
Delegate Sub textUpdater(newText as String)

Public Sub updateTextBox1(newText As String)
    ' The "InvokeRequired" property is thread-safe for reading.
    ' It returns false whenever we call it from the thread that owns the control,
    ' true when we are in a different thread.
    If Me.TextBox1.InvokeRequired Then
        ' The form has a thread-safe method called "Invoke" which takes a delegate
        ' and basically asks the form's control thread to execute the specified
        ' function instead of executing it directly.
        Me.Invoke(New textUpdater(AddressOf updateTextBox1), newText)
    Else
        TextBox1.Text = newText
    End If
End Sub

Private Sub Eventlog_changed(ByVal sender As Object, ByVal e As EventLogArgs)
    updateTextBox1(e.GetEntries())
End Sub

What we've got here is a method (updateTextBox1) which can now safely be executed from any thread. If it is being called from the form's own control thread, it just updates the text box. If it is not, it hands its own address to the form's control thread using Invoke so the same function will be called again, this time from the proper thread.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900