WPF Page.Unloaded called after sub has run - wpf

In this scenario when a user changes access to a different business (multi business accounting) it runs through a sub that closes all open tabs and changes the status of any that are still held open in editing mode.
Everything runs really well, the tabs are closed and then the ID for the new business is loaded.
The trouble is the Page.Unloaded event is running after the entire sub has run and is therefore updating the incorrect DB if any pages were left open in the edit mode.
Is there any way to force any pages that were closed as part of the sub to run unloaded before completing the rest of the code?
Thanks
Edit re comment from Pragmateek
This is an exampled of the unloaded event
Private Sub Website_WebPage_Page_Unloaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Unloaded
Try
Dim SaveUpdateButton As Button = Website_WebPage_Grid.FindName("WebsiteWebPage_SaveUpdateButton")
Dim vScrollViewer As ScrollViewer = Website_WebPage_Grid.FindName("WebsiteWebPage_ScrollViewer")
If NewRecord = True Then
RemoveHandler SaveUpdateButton.Click, AddressOf Website_WebPage_DB_Insert
Else
'Edited record
Dim EditButton As Button = Website_WebPage_Grid.FindName("WebsiteWebPage_EditButton")
Dim EditWebPageButton As Button = Website_WebPage_Grid.FindName("Website_WebPage_EditWebPageButton")
RemoveHandler EditButton.Click, AddressOf Website_WebPage_ToggleEditMode_Click
RemoveHandler vScrollViewer.MouseDoubleClick, AddressOf Website_WebPage_ToggleEditMode_Click
RemoveHandler Me.MouseDown, AddressOf Website_WebPage_MouseDown
RemoveHandler SaveUpdateButton.Click, AddressOf Website_WebPage_DB_Update
RemoveHandler EditWebPageButton.Click, AddressOf WebsiteWebPage_BrowseToClick
End If
If OpenForEdit = True Then
If DB_Functions_ReleaseDT("HOA3_Pages", Page_ID, "Page_ID") = False Then
EditForm_Error()
Else
OpenForEdit = False
End If
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub
If the tab was left in edit mode OpenForEdit will be true and it will call DB_Functions_ReleaseDT
Public Function DB_Functions_ReleaseDT(ByVal TableName As String, ByVal FileID As Integer, ByVal PKey As String) As Boolean
Try
UpdateOpenForEdit(False, TableName)
vService = New Service1Client
strSQL = "UPDATE " & TableName & " SET Open_Editing = 0, Editing_Name = 'System' WHERE " & PKey & " = " & FileID
If vService.InsertDataHOA(strSQL, "3 DB_Functions 43", Current_HOA_ID) = False Then
Return False
Else
Return True
End If
Catch ex As Exception
EmailError(ex)
Return False
Finally
If Not vService Is Nothing Then
vService.Close()
vService = Nothing
End If
End Try
End Function
The problem is Unloaded is running far too late and the variable Current_HOA_ID has changed

Turned out the answer was quite simple - the sub was being run twice, so adding...
e.Handled = True
.. as the first line of code sorted the problem out

Related

WPF - destroy page after unloaded has run

In a WPF app we have the need to sometimes create a new tab that contains a Page inside a Frame..
Once the page has been opened (initialised once) it still seems to stay in navigation history and attempts to load data that may not be relevant at the time.
I have tried a myriad of methods including NavigationService.RemoveBackEntry, but it still persists :-(
This is an example of how the tab/page are opened
Private Sub CashFlow_Edit(sender As Object, e As RoutedEventArgs)
Try
Dim DGV As DGVx = ReportsCashFlow_Grid.FindName("CashFlow_DGV")
e.Handled = True
IsNewRecord = False
If DGV.SelectedItems.Count = 1 Then
Dim row As System.Data.DataRowView = DGV.SelectedItems(0)
Form_ID = row("ID")
Dim vName As String = row("Name")
Dim vTab As STC_Tabx = Application.Current.MainWindow.FindName(TabName)
Dim TabControl As STCx = Application.Current.MainWindow.FindName("AccountingReports_TabControl")
If Not vTab Is Nothing Then
vTab.Close()
End If
Dim MCFrame As New Frame
Dim MCTab As New STC_Tabx
With MCTab
.Name = TabName
.Header = " " & vName & " "
.ImageSource = ReturnImageAsString("Edit.png", 16)
.CloseButtonVisibility = DevComponents.WpfEditors.eTabCloseButtonVisibility.Visible
.TabToolTip = "View or edit the " & vName & " template"
.Content = MCFrame
End With
RemoveHandler MCTab.Closing, AddressOf TabControl_TabClosing
AddHandler MCTab.Closing, AddressOf TabControl_TabClosing
Dim vGrid As Grid = Application.Current.MainWindow.FindName("MainGrid_Accounting")
RegisterControl(vGrid, MCTab)
TabControl.Items.Add(MCTab)
Dim MCPage As New ReportCashFlow_Page
MCFrame.NavigationService.Navigate(MCPage)
LoadedTabs(TabName)
MCTab.IsSelected = True
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub
To remove all the back entries do something like:
while(NavigationService.CanGoBack)
{
NavigationService.RemoveBackEntry();
}
It's not the clean bit of code I would like, but it works - create a global Boolean - when the sub that opens the tab/page is called it's set to true and the loading event will only run the loading code if this is true - it's set to false at the end.
Private Sub ReportCashFlow_Page_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Try
If IsNewTab = False Then
Exit Sub
End If
'Run all the loading code here
Catch ex As Exception
EmailError(ex)
Finally
IsNewTab = False
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() CashFlow_LoadBudget(), SendOrPostCallback), Nothing)
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() ToggleReserve(), SendOrPostCallback), Nothing)
End Try
End Sub

Using a Back Ground worker in VB.Net WPF

I'm new to WPF and the whole threading format. I'm pretty sure I'm close to getting this correct, however, I'm getting the error. "The calling thread cannot access this object because a different thread owns it." I was getting this error before and i had to add the "Dispatcher.BeginInvoke(Sub() LoadLoad())" line, but I'm still getting the error. When i get the error, it highlights the line "Dim dc = New VSCSystemEntities1" in the LoadLoad() sub method. Any help would be appreciated. Thanks.
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
InitialLoad()
End Sub
Private Sub InitialLoad()
Try
loadPayeeTransactionAccounts()
bw.WorkerSupportsCancellation = True
bw.WorkerReportsProgress = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
Catch ex As Exception
MessageBox.Show(ex.Message, "Atlas Error while Loading", MessageBoxButton.OK, MessageBoxImage.Error)
Finally
End Try
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
LoadLoad()
End Sub
Private Sub LoadLoad()
Dispatcher.BeginInvoke(Sub() LoadLoad())
txtSearch.Text = ""
Dim dc = New VSCSystemEntities1
Dim ContainsSearch = New List(Of tblActgPayeeTransactionAccounts2)
Try
ContainsSearch = (From z In dc.tblActgPayeeTransactionAccounts2 Select z Where z.intActgAcctID <> 236).ToList
For Each item In (ContainsSearch)
For i = 1 To ContainsSearch.Count
aList.add(New PayeeTransactions() With {.AccountingAccountID = item.intActgAcctID, .ProgramCode = item.chrPgmCode, _
.CarrierCode = item.intCarrierCode, .DealerNumber = item.chrDlrNum, _
.CoverageCode = item.chrCvgCode, .PayeeType = item.chrPayeeType, .FeeType = item.chrFeeType, .PayeeCode = item.intPayeeCode.ToString, _
.TransactionType = item.chrTransType, .AccountID = item.inbAcctgID, _
.CarrierDescription = item.chrCarrierDesc, .CarrierDescriptionShort = item.chrCarrierDescSht, _
.AccountCustomerID = item.intAcctCustID.ToString, .AccountCode = item.intAcctCo, _
.AccountCodeReceived = item.intAcctCoRec})
System.Threading.Thread.Sleep(200)
bw.ReportProgress(i)
Next
Next
lblCount.Content = ContainsSearch.Count()
dGrid.ItemsSource = Nothing
_ContainsText = CollectionViewSource.GetDefaultView(aList)
dGrid.ItemsSource = _ContainsText
Catch ex As Exception
Throw New Exception("Error: " & ex.Message)
Finally
dc.Dispose()
End Try
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
Me.tbProgress.Text = e.ProgressPercentage.ToString() & "%"
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
If e.Cancelled = True Then
Me.tbProgress.Text = "Canceled!"
ElseIf e.Error IsNot Nothing Then
Me.tbProgress.Text = "Error: " & e.Error.Message
Else
Me.tbProgress.Text = "Done!"
End If
End Sub
Private Sub btnRefresh_Click(sender As Object, e As RoutedEventArgs) Handles btnRefresh.Click
If Not bw.IsBusy = True Then
bw.RunWorkerAsync()
End If
End Sub

WPF Multi-Threading and rotating image

This one has driven me nuts for long enough.
Start a background thread to download data from a webservice on a new thread then show an image on the status bar and change the text.
I have tried with Dispatcher (with each priority) but nothing happens until the threaded sub completes. The closest I can get is implementing the equivalent of DoEvents that at least loads the image and the text, but then the image stops spinning until the thread completes.
Any ideas?
Public Sub Return_DT(ByVal TableName As String)
CurrentDT = TableName
If DownloadingDS Is Nothing Then
DownloadingDS = New Dictionary(Of String, String)
End If
If DownloadingDS.ContainsKey(TableName) = False Then
DownloadingDS.Add(TableName, "Loading")
Else
Exit Sub
End If
Select Case TableName
Case "A_Documents"
strSQL = "SELECT Document_ID, Account_Type, Account_No, Document_Description, Accounts_Only, Open_Editing, Editing_Name, Updated_Name, Updated FROM A_Documents"
Case Else
strSQL = "SELECT * FROM " & TableName
End Select
' Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() LoadMetroImage(), SendOrPostCallback), Nothing)
'Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() ChangeLeftStatusText("Downloading " & CurrentDT & " data..."), SendOrPostCallback), Nothing)
LoadMetroImage()
ChangeLeftStatusText("Downloading " & CurrentDT & " data...")
Application.Current.MainWindow.FindName("MainMetroStatusBar")
Dim vWorker As New BackgroundWorker
AddHandler vWorker.DoWork, AddressOf BackgroundDownload
AddHandler vWorker.RunWorkerCompleted, AddressOf DownloadCompleted
vWorker.RunWorkerAsync()
DoEvents()
End Sub
This is the closest I can get
Public Sub DoEvents()
Dim frame As New DispatcherFrame()
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, New DispatcherOperationCallback(AddressOf ExitFrame), frame)
Dispatcher.PushFrame(frame)
End Sub
Public Function ExitFrame(ByVal f As Object) As Object
CType(f, DispatcherFrame).Continue = False
Return Nothing
End Function
=== EDIT === How Return_DT is called
Public Function DT_Return(ByVal DT As DataTable, ByVal TableName As String) As DataTable
Try
If Not DT Is Nothing Then
If DT_CheckUpdated(TableName) = True Then
Return DT
Else
Return_DT(TableName)
vService = New Service1Client
Dim DS As DataSet = vService.ReturnDataSet("SELECT * FROM " & TableName, Current_HOA_ID)
Dim vDT As DataTable = DS.Tables(0).Copy
DS.Dispose()
Return vDT
End If
Else
Return_DT(TableName)
vService = New Service1Client
Dim DS As DataSet = vService.ReturnDataSet("SELECT * FROM " & TableName, Current_HOA_ID)
Dim vDT As DataTable = DS.Tables(0).Copy
DS.Dispose()
Return vDT
End If
Catch ex As Exception
EmailError(ex)
Return Nothing
End Try
End Function
I found the solution was to use Dispatcher.Timer in the UI thread to check every second to see if the DT had been downloaded..
Public Function DT_Return(ByVal DT As DataTable, ByVal TableName As String) As DataTable
Try
If Not DT Is Nothing Then
If DT_CheckUpdated(TableName) = True Then
Return DT
Else
CurrentlyDownloading = True
Return_DT(TableName)
Dim vTimer As New DispatcherTimer
vTimer.Interval = TimeSpan.FromMilliseconds(1000)
AddHandler vTimer.Tick, Sub(sender As Object, e As EventArgs)
Do While CurrentlyDownloading = True
Loop
vTimer.Stop()
End Sub
vTimer.Start()
Return DT
End If
Else
CurrentlyDownloading = True
Return_DT(TableName)
Dim vTimer As New DispatcherTimer
vTimer.Interval = TimeSpan.FromMilliseconds(1000)
AddHandler vTimer.Tick, Sub(sender As Object, e As EventArgs)
Do While CurrentlyDownloading = True
Loop
vTimer.Stop()
End Sub
vTimer.Start()
Return DT
End If
Catch ex As Exception
EmailError(ex)
Return Nothing
End Try
End Function

How to cancel BackgroundWorker in WPF without DoEvents

I have a search box that works great in WinForms, but is giving me trouble in WPF.
It works by starting a search each time a letter is pushed, similar to Google.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
If SearchWorker.IsBusy Then
SearchWorker.CancelAsync()
Do While SearchWorker.IsBusy
'Application.DoEvents()
'System.Threading.Thread.Sleep(500)
Loop
End If
doSearchText = txtQuickSearch.Text
SearchWorker.RunWorkerAsync()
End If
Every time a key is pushed it cancels the current searchWorker then restarts it. In WinForms the Do while searchworker.isbusy doevents loop worked great, but since I don't have access to that anymore, I need to figure out a better way to do it. Sleep() deadlocks it, and I've tried just doing i+=1 as a way to pass time until it's not busy, but that doesn't work either...
What should I do?
Update: Here's what I changed it to. It works, but the cancel part doesn't seem to ever trigger, this doesn't seem to be running async...
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Imports System.Threading
Imports System.Threading.Tasks
Public Class QuickSearch
Private doSearchText As String
Private canceled As Boolean
Private curSearch As String
Dim searchResults As New ObservableCollection(Of ocSearchResults)
'Task Factory
Private cts As CancellationTokenSource
Private searchtask As Task(Of ObservableCollection(Of ocSearchResults))
Private Sub txtQuickSearch_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.KeyEventArgs) Handles txtQuickSearch.KeyDown
If e.Key = Key.Enter Then
curSearch = ""
End If
If ((txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter)) And Not txtQuickSearch.Text = curSearch Then
If Not cts Is Nothing Then
cts.Cancel()
ColorChecker.CancelAsync()
Try
' searchtask.Wait()
Catch ex As AggregateException
MsgBox(ex.InnerException.Message)
End Try
cts = New CancellationTokenSource
Else
cts = New CancellationTokenSource
End If
Dim cToken = cts.Token
Me.Height = 400
doSearchText = txtQuickSearch.Text
'This always completes fully before continuing on to tRunWorkerComplete(searchtask.Result) '
searchtask = Task(Of ObservableCollection(Of ocSearchResults)).Factory.StartNew(Function() tPerformSearch(cToken), cToken)
Try
tRunWorkerCompleted(searchtask.Result)
Catch ex As AggregateException
' MsgBox(ex.InnerException.Message)
End Try
Else
If Not cts Is Nothing Then
cts.Cancel()
End If
searchResults.Clear()
End If
End Sub
Function tPerformSearch(ByVal ct As CancellationToken) As ObservableCollection(Of ocSearchResults)
On Error GoTo sError
canceled = False
If curSearch = doSearchText Then
canceled = True
Return Nothing
End If
curSearch = doSearchText
Dim SR As New ObservableCollection(Of ocSearchResults)
Dim t As ocSearchResults
Dim rSelect As New ADODB.Recordset
Dim sSql As String = "SELECT DISTINCT CustomerID, CustomerName, City, State, Zip FROM qrySearchFieldsQuick WHERE "
Dim sWhere As String = "CustomerName Like '" & doSearchText & "%'"
SR.Clear()
With rSelect
.Open(sSql & sWhere & " ORDER BY CustomerName", MyCn, ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockReadOnly)
Do While Not .EOF
If ct.IsCancellationRequested Then ' This never shows true, the process always returns a value, as if it wasn't async'
canceled = True
Return Nothing
End If
Do While IsDBNull(.Fields("CustomerID").Value)
.MoveNext()
Loop
t = New ocSearchResults(.Fields!CustomerID.Value, .Fields!CustomerName.Value.ToString.Trim, .Fields!City.Value.ToString.Trim, .Fields!State.Value.ToString.Trim, .Fields!Zip.Value.ToString.Trim)
If Not SR.Contains(t) Then
SR.Add(t)
End If
aMoveNext:
.MoveNext()
Loop
.Close()
End With
Return SR
Exit Function
sError:
MsgBox(ErrorToString, MsgBoxStyle.Exclamation)
End Function
Sub tRunWorkerCompleted(ByVal SR As ObservableCollection(Of ocSearchResults))
If canceled Then
Exit Sub
End If
If cts.IsCancellationRequested Then
Exit Sub
End If
searchResults.Clear()
For Each t As ocSearchResults In SR
searchResults.Add(t)
Next
ColorChecker = New BackgroundWorker
ColorChecker.WorkerReportsProgress = True
ColorChecker.WorkerSupportsCancellation = True
ColorChecker.RunWorkerAsync(searchResults)
lblRecordCount.Text = "(" & searchResults.Count & ") Records"
progBar.Value = 100
Exit Sub
sError:
MsgBox(ErrorToString)
End Sub
I don't know enough VB to give you any well written sample code, but if you're on .Net 4.0 I suggest switching to the System.Threading.Tasks namespace, which has cancellation abilities.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
If TokenSource Is Not Nothing Then
TokenSource.Cancel()
TokenSource = New CancellationTokenSource()
End If
Task.Factory.StartNew(SomeSearchMethod, txtQuickSearch.Text, TokenSource.Token)
End If
I am not sure a BackgroundWorker is flexible enough to provide an elegant solution for this type of background processing anyway. I think what I would do is to create a single dedicated thread for doing the searching. This thread would use the producer-consumer pattern for accepting work items and processing them.
The following code is a rough sketch of how I see this strategy working. You would call the SearchAsync method to request a new search. That method accepts a callback that gets invoked when and if the search operation found something. Notice that the consumer code (in the Run method) cancels its current search operation if another search request is queued. The effect is that the consumer only ever processes the latest request.
Public Class Searcher
Private m_Queue As BlockingCollection(Of WorkItem) = New BlockingCollection(Of WorkItem)()
Public Sub New()
Dim t = New Thread(AddressOf Run)
t.IsBackground = True
t.Start()
End Sub
Public Sub SearchAsync(ByVal text As String, ByVal callback As Action)
Dim wi = New WorkItem()
wi.Text = text
wi.Callback = callback
m_Queue.Add(wi)
End Sub
Private Sub Run()
Do While True
Dim wi As WorkItem = m_Queue.Take()
Dim found As Boolean = False
Do While Not found AndAlso m_Queue.Count = 0
' Continue searching using your custom algorithm here.
Loop
If found Then
wi.Callback()
End If
Loop
End Sub
Private Class WorkItem
Public Text As String
Public Callback As Action
End Class
End Class
Here is where the elegance happens. Look at how you can implement the logic from the UI thread now.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
searcher.SearchAsync(txtQuickSearch.Text, AddressOf OnSearchComplete)
End If
Note that OnSearchComplete will be executed on the worker thread so you will need to call Dispatcher.Invoke from within the callback if you want to publish the results to a UI control.
You can simulate a DoEvents in WPF by doing (in C#):
Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => {}));

UI not updating fast

Good evening,
Following is the code I used for reading the files and folders from a drive etc.
Public Class LoadingBox
Public counter As ULong
Public OpenRecords As New Dictionary(Of String, MainWindow.records)
Public Path As String
Public Diskname As String
Private WithEvents BKWorker As New BackgroundWorker()
Public Sub New(ByVal _Path As String, ByVal _Diskname As String)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Path = _path
Diskname = _diskname
End Sub
Private Sub GetStructure(ByVal tempdir As String, ByVal ParentID As String, ByVal DiskName As String)
Dim maindir As DirectoryInfo = My.Computer.FileSystem.GetDirectoryInfo(tempdir)
For Each Dir As DirectoryInfo In maindir.GetDirectories
Try
Dim d As New MainWindow.records
d.Filename = Dir.Name
d.Folder = True
d.Rowid = Date.UtcNow.ToString() + counter.ToString()
d.Size = 0
d.ParentID = ParentID
d.DiskName = DiskName
d.DateCreated = Dir.CreationTimeUtc
d.DateModified = Dir.LastWriteTimeUtc
OpenRecords.Add(d.Rowid, d)
'Label1.Content = "Processing: " + Dir.FullName
BKWorker.ReportProgress(0, Dir.FullName)
counter = counter + 1
GetStructure(Dir.FullName, d.Rowid, DiskName)
Catch ex As Exception
End Try
Next
For Each fil As FileInfo In maindir.GetFiles
Try
Dim d As New MainWindow.records
d.Filename = fil.Name
d.Folder = False
d.Rowid = Date.UtcNow.ToString() + counter.ToString()
d.Size = fil.Length
d.ParentID = ParentID
d.DiskName = DiskName
d.DateCreated = fil.CreationTimeUtc
d.DateModified = fil.LastWriteTimeUtc
OpenRecords.Add(d.Rowid, d)
'Label1.Content = "Processing: " + fil.FullName
BKWorker.ReportProgress(0, fil.FullName)
counter = counter + 1
Catch ex As Exception
End Try
Next
End Sub
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
counter = 0
BKWorker.WorkerReportsProgress = True
AddHandler BKWorker.DoWork, AddressOf BKWorker_Do
AddHandler BKWorker.ProgressChanged, AddressOf BKWorker_Progress
AddHandler BKWorker.RunWorkerCompleted, AddressOf BKWorker_Completed
BKWorker.RunWorkerAsync()
'GetStructure(Path, "0", Diskname)
End Sub
Private Sub BKWorker_Do(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'Throw New NotImplementedException
GetStructure(Path, "0", Diskname)
End Sub
Private Sub BKWorker_Progress(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
'Throw New NotImplementedException
Label1.Content = "Processing: " + e.UserState.ToString()
If ProgressBar1.Value = 100 Then
ProgressBar1.Value = 0
End If
ProgressBar1.Value = ProgressBar1.Value + 1
End Sub
Private Sub BKWorker_Completed(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
'Throw New NotImplementedException
MessageBox.Show("Completed")
Me.Close()
End Sub
End Class
However the problem is that, the background thread is able to read files very fast, but the UI thread is not able to keep up the speed with it, could you please advice me on how I can solve this issue.
You almost never want to report progress on every single item when you're iterating through that many items.
I would suggest finding some reasonable number of files to wait for before reporting progress. Every 5th or every 10th or so on. You probably want to take a look at what your normal number of files is. In other words, if you're normally processing only 25 files, you probably don't want to only update every 10 files. But if you're normally processing 25000 files, you could maybe even only update every 100 files.
One quick answer would be to only report the progress when a certain amount of time has passed that way if 10 files were processed in that time the UI isn't trying to update one each one. If things are processing that fast then you really don't need to update the user on every single file.
Also on a quick side note if your ProgressBar isn't actually reporting progress from 0 to 100% you might want to just set its IsIndeterminate property to true instead of increasing the percent and then resetting it back to 0.

Resources