Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compact Framework/Threading - MessageBox displays over other controls after option is chosen

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:

  • Clicks button
  • Method checks for updates, count is returned.
  • If greater than 0, then ask the user if they want to install using MessageBox.Show().
  • If yes, it runs through a loop and call BeginInvoke() on the run() method of each update to run it in the background.
  • My update class has some events that are used to update a progress bar etc.

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).

  • What should I do to make the messagebox disappear instantly before the update loop starts?
  • Should I be using Threads instead of BeginInvoke()?
  • Should I be doing the initial update check on a separate thread and calling MessageBox.Show() from that thread?

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

Windows Mobile Bug

like image 939
Brian Lyttle Avatar asked Mar 02 '23 08:03

Brian Lyttle


2 Answers

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.

like image 126
John Sibly Avatar answered Mar 04 '23 03:03

John Sibly


Have you tried putting a

Application.DoEvents()

in here

if (dlgRes == DialogResult.Yes)
{
   Application.DoEvents(); 
   ProcessAllUpdates(um2); 
}
like image 41
JamesSugrue Avatar answered Mar 04 '23 02:03

JamesSugrue