I'm working on an app that grabs and installs a bunch of updates off an an external server, and need some help with threading. The user follows this process:
The progress bar updates are fine, but the MessageBox is not fully cleared from the screen because the update loop starts right after the user clicks yes (see screenshot below).
Code
// Button clicked event handler code...
DialogResult dlgRes = MessageBox.Show(
string.Format("There are {0} updates available.\n\nInstall these now?",
um2.Updates.Count), "Updates Available",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2
);
if (dlgRes == DialogResult.Yes)
{
ProcessAllUpdates(um2);
}
// Processes a bunch of items in a loop
private void ProcessAllUpdates(UpdateManager2 um2)
{
for (int i = 0; i < um2.Updates.Count; i++)
{
Update2 update = um2.Updates[i];
ProcessSingleUpdate(update);
int percentComplete = Utilities.CalculatePercentCompleted(i, um2.Updates.Count);
UpdateOverallProgress(percentComplete);
}
}
// Process a single update with IAsyncResult
private void ProcessSingleUpdate(Update2 update)
{
update.Action.OnStart += Action_OnStart;
update.Action.OnProgress += Action_OnProgress;
update.Action.OnCompletion += Action_OnCompletion;
//synchronous
//update.Action.Run();
// async
IAsyncResult ar = this.BeginInvoke((MethodInvoker)delegate() { update.Action.Run(); });
}
Screenshot
Your UI isn't updating because all the work is happening in the user interface thread. Your call to:
this.BeginInvoke((MethodInvoker)delegate() {update.Action.Run(); })
is saying invoke update.Action.Run() on the thread that created "this" (your form), which is the user interface thread.
Application.DoEvents()
will indeed give the UI thread the chance to redraw the screen, but I'd be tempted to create new delegate, and call BeginInvoke on that.
This will execute the update.Action.Run() function on a seperate thread allocated from the thread pool. You can then keep checking the IAsyncResult until the update is complete, querying the update object for its progress after every check (because you can't have the other thread update the progress bar/UI), then calling Application.DoEvents().
You also are supposed to call EndInvoke() afterwards otherwise you may end up leaking resources
I would also be tempted to put a cancel button on the progress dialog, and add a timeout, otherwise if the update gets stuck (or takes too long) then your application will have locked up forever.
Have you tried putting a
Application.DoEvents()
in here
if (dlgRes == DialogResult.Yes)
{
Application.DoEvents();
ProcessAllUpdates(um2);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With