Solved

VB.Net Updating UI from Seperate thread

Posted on 2012-12-30
6
1,218 Views
Last Modified: 2013-01-02
Hi Experts.   I have a module that creates a new thread and need to take the output from that thread to update textboxes on a form.   Usual methods dont work and I've tried creating an update sub on the form and sending the data to that first but this also doesnt work.  

From research on Google I see that this is a known issue usually resolved with a backgroundworker. Unfortunately, I cant seem to find any examples that arnt horrendously complicated.   Could someone please supply an example I can get started with?  

Its literally just appending a few variables to a multiline textbox and three normal textboxes.  Any help would be appreciated.
0
Comment
Question by:PNRT
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
6 Comments
 
LVL 17

Expert Comment

by:nepaluz
ID: 38730595
We need to see some code to be able to suggest code that is appropriate to your situation, however, here's an example I gave to a previous question.
Public Class Form1
    WithEvents xworker As New System.ComponentModel.BackgroundWorker
    Sub New()
        Dim runGet As New SomeClass
        With runGet
            .id = 5
        End With
        xworker.RunWorkerAsync(runGet)

    End Sub

    Private Sub xWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles xworker.DoWork
        Dim qWorker = CType(sender, System.ComponentModel.BackgroundWorker)
        Dim WC As SomeClass = CType(e.Argument, SomeClass)
        WC.DoSomething(qWorker, e)
    End Sub

    Private Sub xWorker_Progress(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles xworker.ProgressChanged
        'we expect a string as a progress return value which you can update the ui with
        Dim ResString As String = e.UserState

    End Sub

    Private Sub xWorker_Completed(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles xworker.RunWorkerCompleted
        'here we expect a list of string which you can update the ui with
        Dim ResList As List(Of String) = e.Result
    End Sub
End Class

Public Class SomeClassInsideYourModule

    Public name As String = ""
    Public id As Integer = 0

    Public Sub DoSomething(ByVal worker As System.ComponentModel.BackgroundWorker, ByVal e As System.ComponentModel.DoWorkEventArgs)
        worker.WorkerReportsProgress = True
        'here test progress
        name = "Progress Name"
        worker.ReportProgress(0, name)
        'here test result
        name = "Result Name"
        id = 15
        e.Result = New List(Of String) From {name, id}
    End Sub
End Class

Open in new window

0
 
LVL 2

Author Comment

by:PNRT
ID: 38730645
Sorry should have realised.   The references to Textbox 3 4 and 5 is what I'm trying to update on the form.  Thanks for you help.


Imports System.Threading
Imports System.IO
Module AnalyzeFiles
   
    Public Sub StartAnalyze()
         Dim t As Thread
        t = New Thread(AddressOf AnalyzeFiles.backgroundprocess)
        t.Start()
    End Sub

    Private Sub backgroundprocess()
        SelectedFolderPath = Form1.TextBox1.Text
        OutputPath = Form1.TextBox2.Text
        Using sw As New System.IO.StreamWriter(OutputPath)
            FindFiles(SelectedFolderPath, sw)
        End Using
    End Sub

    Private Sub FindFiles(ByVal folder As String, ByVal sw As System.IO.StreamWriter)
        Dim dt As DateTime = DateTime.Today
        Dim di As System.IO.DirectoryInfo
        Dim foldersToProcess As New List(Of System.IO.DirectoryInfo)
        foldersToProcess.Add(New System.IO.DirectoryInfo(folder))
        While foldersToProcess.Count > 0
            di = foldersToProcess(0)
            foldersToProcess.RemoveAt(0)

            Try
                For Each fi As System.IO.FileInfo In di.GetFiles  
                  NumberOfFiles = NumberOfFiles + 1
                    sw.WriteLine(fi.FullName)
                 Next
            Catch ex As Exception
      Textbox3.appendtext("Cannot access folder: "  &  fi.fullname)
                  NumberOfErrors = NumberOfErrors + 1
            End Try

                  For Each subDi As System.IO.DirectoryInfo In di.GetDirectories
                    foldersToProcess.Add(subDi)
                Next
      
      Textbox4.text = NumberofFiles.tostring
      Textbox5.text = NumberofErrors.tostring

        End While

   End Sub
End Module
0
 
LVL 17

Accepted Solution

by:
nepaluz earned 500 total points
ID: 38730680
I have not checked your logic in the code, but made some suttle changes anyway to suit the background worker. I changed the updating of the number of files and errors to be done at the end of the process rather than during every loop. Here is the background worker class that you can put inside your module.
    Public Class AnalyzeFiles

        Public sw As System.IO.StreamWriter
        Public folder As String

        Public Sub DoSomething(ByVal worker As System.ComponentModel.BackgroundWorker, ByVal e As System.ComponentModel.DoWorkEventArgs)
            worker.WorkerReportsProgress = True
            Dim NumberOfFiles As Integer = 0
            Dim NumberOfErrors As Integer = 0
            Dim erFile As String = Nothing
            Dim dt As DateTime = DateTime.Today
            Dim di As System.IO.DirectoryInfo
            Dim foldersToProcess As New List(Of System.IO.DirectoryInfo)
            foldersToProcess.Add(New System.IO.DirectoryInfo(folder))
            While foldersToProcess.Count > 0
                di = foldersToProcess(0)
                foldersToProcess.RemoveAt(0)
                Try
                    For Each fi As System.IO.FileInfo In di.GetFiles
                        erFile = fi.FullName
                        NumberOfFiles = NumberOfFiles + 1
                        sw.WriteLine(erFile)
                    Next
                Catch ex As Exception
                    worker.ReportProgress(0, "Cannot access folder: " & erFile)
                    NumberOfErrors = NumberOfErrors + 1
                End Try

                For Each subDi As System.IO.DirectoryInfo In di.GetDirectories
                    foldersToProcess.Add(subDi)
                Next
            End While
            e.Result = New List(Of Integer) From {NumberOfFiles, NumberOfErrors}
        End Sub
    End Class

Open in new window

Next you will need to declare a background worker in your main form (or where-ever you want to invoke your routine from) and handle its events (i.e do_work, progress changed and completed). Here is the code:
First declare the worker
WithEvents xworker As New System.ComponentModel.BackgroundWorker

Open in new window

Then the handlers:
    Private Sub xWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles xworker.DoWork
        Dim qWorker = CType(sender, System.ComponentModel.BackgroundWorker)
        Dim WC As AnalyzeFiles = CType(e.Argument, AnalyzeFiles)
        WC.DoSomething(qWorker, e)
    End Sub

    Private Sub xWorker_Progress(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles xworker.ProgressChanged
        'here you can update your textbox3
        Textbox3.appendtext(e.UserState)

    End Sub

    Private Sub xWorker_Completed(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles xworker.RunWorkerCompleted
        'since we expect a list of integer with NumberOfFiles at index 0 and NumberOfError at index 1
        Dim ResList As List(Of Integer) = e.Result
        Textbox4.text = ResList.First.ToString
        Textbox5.text = ResList.Last.ToString
    End Sub

Open in new window

Finally, to call the background worker, simply add the line:
xWorker.RunWorkerAsync()

Open in new window

0
Salesforce Has Never Been Easier

Improve and reinforce salesforce training & adoption using WalkMe's digital adoption platform. Start saving on costly employee training by creating fast intuitive Walk-Thrus for Salesforce. Claim your Free Account Now

 
LVL 17

Expert Comment

by:nepaluz
ID: 38730692
PS.
1. You can always intiate a streamwriter within the class and not have to pass it to the class.
2. Declare the backgroundworker (and add the handlers) in the form where the textboxes that you want updated reside. (You can also add the backgroundworker through the designer)
3. If you want to update textboxes 4 and 5 during the loop, you can always change the report progress routine to suit, e.g
While foldersToProcess.Count > 0
    ....
    Catch ex as Exception
        worker.ReportProgress(0, "Cannot access folder: " & erFile)
    End Try
    ....
   worker.ReportProgress(10, New List(Of Integer) From {NumberOfFiles, NumberOfErrors}
)
End While

Open in new window

The simply change the progress handler to filter on the progress percentage, 0 for updating textbox3 and 10 for updating textboxes 4 and 5 (also bear in mind the different types being returned by each percentage, i.e string and list of integer respectively)
0
 
LVL 70

Expert Comment

by:Éric Moreau
ID: 38730778
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 38731450
Here's another approach without the backgroundworker, using a class that raises events.  The events are marshaled to the main UI thread using a SynchronizationContext:
Public Class Form1

    Private Sub btnSetStartFolder_Click(sender As System.Object, e As System.EventArgs) Handles btnSetStartFolder.Click
        Using fbd As New FolderBrowserDialog
            If fbd.ShowDialog = Windows.Forms.DialogResult.OK Then
                TextBox1.Text = fbd.SelectedPath
            End If
        End Using
    End Sub

    Private Sub btnSetOutpuFile_Click(sender As System.Object, e As System.EventArgs) Handles btnSetOutpuFile.Click
        Using sfd As New SaveFileDialog
            If sfd.ShowDialog = Windows.Forms.DialogResult.OK Then
                TextBox2.Text = sfd.FileName
            End If
        End Using
    End Sub

    Private Sub btnStartFindFiles_Click(sender As System.Object, e As System.EventArgs) Handles btnStartFindFiles.Click
        btnStartFindFiles.Enabled = False
        TextBox3.Clear()
        TextBox4.Clear()
        TextBox5.Clear()

        Dim FF As New FindFiles(TextBox1.Text, TextBox2.Text)
        AddHandler FF.FindFilesError, AddressOf FF_FindFilesError
        AddHandler FF.FindFilesProgress, AddressOf FF_FindFilesProgress
        AddHandler FF.FindFilesFailed, AddressOf FF_FindFilesFailed
        AddHandler FF.FindFilesComplete, AddressOf FF_FindFilesComplete
        FF.StartAnalyze()
    End Sub

    Private Sub FF_FindFilesError(FileName As String)
        TextBox3.AppendText(FileName & vbCrLf)
    End Sub

    Private Sub FF_FindFilesProgress(NumberOfFiles As Integer, NumberOfErrors As Integer)
        TextBox4.Text = NumberOfFiles
        TextBox5.Text = NumberOfErrors
    End Sub

    Private Sub FF_FindFilesFailed(Failure As String)
        MessageBox.Show(Failure, "FindFiles() Failed!")
    End Sub

    Private Sub FF_FindFilesComplete()
        MessageBox.Show("Done!", "FindFiles() Complete!")
        btnStartFindFiles.Enabled = True
    End Sub

End Class

Public Class FindFiles

    Private _OutputPath As String = ""
    Private _SelectedFolderPath As String = ""
    Private T As System.Threading.Thread = Nothing
    Private SC As System.Threading.SynchronizationContext = Nothing

    Public Event FindFilesError(ByVal FileName As String)
    Public Event FindFilesProgress(ByVal NumberOfFiles As Integer, ByVal NumberOfErrors As Integer)
    Public Event FindFilesFailed(ByVal Failure As String)
    Public Event FindFilesComplete()

    Private Sub New()
    End Sub

    Public Sub New(ByVal SelectedFolderPath As String, ByVal OutputPath As String)
        _OutputPath = OutputPath
        _SelectedFolderPath = SelectedFolderPath
        SC = System.Windows.Forms.WindowsFormsSynchronizationContext.Current
    End Sub

    Public Sub StartAnalyze()
        If IsNothing(T) Then
            T = New System.Threading.Thread(AddressOf BackgroundProcess)
            T.Start()
        End If
    End Sub

    Private Sub BackgroundProcess()
        Try
            Using sw As New System.IO.StreamWriter(_OutputPath)
                FindFiles(_SelectedFolderPath, sw)
            End Using
        Catch ex As Exception
            If Not IsNothing(SC) Then
                SC.Post(New System.Threading.SendOrPostCallback(AddressOf RaiseFailed), ex.Message)
            End If
        End Try

        If Not IsNothing(SC) Then
            SC.Post(New System.Threading.SendOrPostCallback(AddressOf RaiseComplete), Nothing)
        End If
    End Sub

    Private Sub FindFiles(ByVal folder As String, ByVal sw As System.IO.StreamWriter)
        Dim NumberOfFiles As Integer = 0
        Dim NumberOfErrors As Integer = 0

        Dim dt As DateTime = DateTime.Today
        Dim di As System.IO.DirectoryInfo
        Dim fi As System.IO.FileInfo
        Dim foldersToProcess As New List(Of System.IO.DirectoryInfo)
        Try
            foldersToProcess.Add(New System.IO.DirectoryInfo(folder))
        Catch ex As Exception
            If Not IsNothing(SC) Then
                SC.Post(New System.Threading.SendOrPostCallback(AddressOf RaiseFailed), ex.Message)
            End If
            Exit Sub
        End Try

        While foldersToProcess.Count > 0
            di = foldersToProcess(0)
            foldersToProcess.RemoveAt(0)

            Try
                For Each fi In di.GetFiles
                    NumberOfFiles = NumberOfFiles + 1
                    sw.WriteLine(fi.FullName)
                Next
            Catch ex As Exception
                If Not IsNothing(SC) Then
                    SC.Post(New System.Threading.SendOrPostCallback(AddressOf RaiseError), di.FullName)
                End If
                NumberOfErrors = NumberOfErrors + 1
            End Try

            Try
                foldersToProcess.AddRange(di.GetDirectories)
            Catch ex As Exception
                If Not IsNothing(SC) Then
                    SC.Post(New System.Threading.SendOrPostCallback(AddressOf RaiseError), di.FullName)
                End If
            End Try

            If Not IsNothing(SC) Then
                SC.Post(New System.Threading.SendOrPostCallback(AddressOf RaiseProgress), New Integer() {NumberOfFiles, NumberOfErrors})
            End If
        End While
    End Sub

    Private Sub RaiseError(ByVal state As Object)
        ' *** DO NOT CALL DIRECTLY! ***   
        ' Already Post()ed with the SynchronizationContext:
        RaiseEvent FindFilesError(state)
    End Sub

    Private Sub RaiseProgress(ByVal state As Object)
        ' *** DO NOT CALL DIRECTLY! ***   
        ' Already Post()ed with the SynchronizationContext:
        Dim values() As Integer = CType(state, Integer())
        RaiseEvent FindFilesProgress(values(0), values(1))
    End Sub

    Private Sub RaiseFailed(ByVal state As Object)
        ' *** DO NOT CALL DIRECTLY! ***   
        ' Already Post()ed with the SynchronizationContext:
        RaiseEvent FindFilesFailed(state.ToString)
    End Sub

    Private Sub RaiseComplete(ByVal state As Object)
        ' *** DO NOT CALL DIRECTLY! ***   
        ' Already Post()ed with the SynchronizationContext:
        RaiseEvent FindFilesComplete()
    End Sub

End Class

Open in new window

0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
MYSQL responding very slow 3 51
Visual Studio hangs on running project 6 63
VB.Net creating Contact in Outlook 1 60
Need help converting bitmap to image in VB.Net 8 47
Well, all of us have seen the multiple EXCEL.EXE's in task manager that won't die even if you call the .close, .dispose methods. Try this method to kill any excels in memory. You can copy the kill function to create a check function and replace the …
A while ago, I was working on a Windows Forms application and I needed a special label control with reflection (glass) effect to show some titles in a stylish way. I've always enjoyed working with graphics, but it's never too clever to re-invent …
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…

730 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question