Reputation: 411
I didn't think this actually happened, but it appears that my WPF app (window) is not calling a Sub asynchronously, or something that looks like this... The use case is deceivingly simple:
wpfw = WPF Window
StatusLeft = textbox on wpfw
cmds = Command class instantiated in wpfw class to store all window commands (subs). the wpfw instance is passed to it in the constructor.
UpdateStatusLeft = a Sub in cmds that looks like this:
Private Sub UpdateStatusLeft(UpdateText As String)
Try
Me.wpfw.Dispatcher.Invoke(CType(Sub() Me.wpfw.StatusLeft.Text = UpdateText, Action))
Catch ex As Exception
[...]
End Try
End Sub
The above works, but only updates the main window after the long running sub in cmds is finished. Calling that long running command is nothing special:
(XAML)
<MenuItem Header="Process Files" Click="mnuReference_ProcessFiles"/>
in wpfw, the handler for the click (mnuReference_ProcessFiles) is this:
Private Sub mnuReference_ProcessFiles(sender As Object, e As RoutedEventArgs)
Me.cmds.ParseFiles()
End Sub
Now cmds is instantiated as soon as the wpfw is, and the sub it is pointing to (mnuReference_ProcessFiles) looks like this:
Public Sub ParseFiles() Implements Icmds.ParseFiles
Try
Dim fu As FileUtils = New FileUtils()
Me.UpdateStatusLeft("Starting Batch 1...")
ipu.ParseFile(".\batch1")
Me.UpdateStatusLeft("Starting Batch 2...")
ipu.ParseFile(".\batch2")
[...]
Above, "Me.UpdateStatusLeft" is in the cmds class. In fact, I put a Sub UpdateStatusLeft in every class the mainwindow calls (including it's own class!), and I pass down the wpfc instance to each command/processing class.
If you try to update the textarea directly frm another thread/class other than the wpfw one, you get a thread error-which is why I use Dispatcher.Invoke(...
Clarity:
The commands are all firing off as expected, and do their job well. That never was an issue.
The issue is trying to find a way to update the top/originating UI thread's textarea as those tasks progress so the user doesn't fall asleep or think the program has crashed, etc.
Upvotes: 0
Views: 833
Reputation: 132548
The main application dispatcher uses the UI thread, so long-running processes will still lock up the UI thread
The typical solution is to run long-running processes in a background thread, then use the dispatcher to update your UI objects on the main thread once it's finished.
Or if you use something like the Task Parallel Library, it allows you to schedule your task for UI thread, and bypass the dispatcher completely. Here's an example:
Task.Factory
.StartNew(() =>
{
// This runs in a background thread
return GetLongRunningProcessResults();
})
.ContinueWith.StartNew((t) =>
{
// This runs in the UI thread because of the
// TaskScheduler.FromCurrentSynchronizationContext parameter
UpdateUI(t.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
Upvotes: 4
Reputation: 9265
Wrap the code in ParseFiles
in a call to Tasks.Run.
If you want your application to stay responsive, you must execute intensive tasks in another thread than the main thread.
Upvotes: 0