PNRT
asked on
VB.Net Updating UI from Seperate thread
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.
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.
ASKER
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.backgroundpro cess)
t.Start()
End Sub
Private Sub backgroundprocess()
SelectedFolderPath = Form1.TextBox1.Text
OutputPath = Form1.TextBox2.Text
Using sw As New System.IO.StreamWriter(Out putPath)
FindFiles(SelectedFolderPa th, 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(fo lder))
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("Canno t 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
Imports System.Threading
Imports System.IO
Module AnalyzeFiles
Public Sub StartAnalyze()
Dim t As Thread
t = New Thread(AddressOf AnalyzeFiles.backgroundpro
t.Start()
End Sub
Private Sub backgroundprocess()
SelectedFolderPath = Form1.TextBox1.Text
OutputPath = Form1.TextBox2.Text
Using sw As New System.IO.StreamWriter(Out
FindFiles(SelectedFolderPa
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(fo
While foldersToProcess.Count > 0
di = foldersToProcess(0)
foldersToProcess.RemoveAt(
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("Canno
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
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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
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
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)
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