Question

While developing a solution on Windows Forms I went into a routine of showing continuous progress to user. I implemented simple dummy window with continuous progress bar:

Continuos progress window

In solution tree it is situated on the same level as the Main Window:

Windows forms solution tree

The simplest working approach to show continuous progress while doing something is the following code. It does work:

//This method works
private void DoSomeBackgroundStuffWithShow()
{
    ContinuousProgressWindow continuousProgressWindow = 
        new ContinuousProgressWindow();
    BackgroundWorker backgroundWorker = new BackgroundWorker();
    backgroundWorker.DoWork += (sender, arguments) =>
    {
        //Do some  stuff for 4 seconds
        Thread.Sleep(4000);
    };
    backgroundWorker.RunWorkerCompleted += (sender, arguments) =>
    {
        //Window is closed when needed. Great!
        continuousProgressWindow.Dispose(); 
    };
    continuousProgressWindow.Show(this);
    backgroundWorker.RunWorkerAsync();
}

But I need this window to appear topmost and block its parent while working. The following code is quite similar, and it does not work - the dialog is shown, but never closed:

//This method DOES NOT WORK
private void DoSomeBackgroundStuffWithShowDialog()
{
    ContinuousProgressWindow continuousProgressWindow =
        new ContinuousProgressWindow();
    BackgroundWorker backgroundWorker = new BackgroundWorker();
    backgroundWorker.DoWork += (sender, arguments) =>
    {
        //Do some important stuff for 4 seconds
        Thread.Sleep(4000);
    };
    backgroundWorker.RunWorkerCompleted += (sender, arguments) =>
    {
        //None of the following work for "ShowDialog() method"
        //Ran with debugger - breakpoints not hit!
        continuousProgressWindow.DialogResult = DialogResult.OK;
        continuousProgressWindow.Close();
        continuousProgressWindow.Dispose();
    };
    continuousProgressWindow.ShowDialog(this);
    backgroundWorker.RunWorkerAsync();
}

Then, I realize the problem is about UI threads flow: when the progress window is ran as a dialog, MainWindow thread is frozen and it cannot be invoked by BackgroundWorker in RunWorkerCompleted delegate to close the dialog.

What is the simplest solution to make it work as wanted?

Était-ce utile?

La solution

  continuousProgressWindow.ShowDialog(this);
  backgroundWorker.RunWorkerAsync();

You've got a simple chicken-and-egg problem, you don't start the worker until after the dialog closes. ShowDialog() is a blocking call. So the RunWorkerCompleted event doesn't fire because the worker didn't get started. The simplest workaround is to swap the two statements:

  backgroundWorker.RunWorkerAsync();
  continuousProgressWindow.ShowDialog(this);

That is not entirely safe to do. Not a problem with this snippet but in real code there is a danger that the worker completes before the dialog is displayed. Low odds but not zero. To solve that, you want to delay the worker until you are sure the dialog is up and running. That can be done with an AutoResetEvent that is Set() by the dialog's OnShown() method. Or, more elegantly, by taking advantage of a trick:

  this.BeginInvoke(new Action(() => backgroundWorker.RunWorkerAsync()));
  continuousProgressWindow.ShowDialog(this);

The delegate target of Control.BeginInvoke() runs when the program re-enters the message loop. That happens after the dialog becomes visible :)

Autres conseils

The issue here is that you are calling continuousProgressWindow.ShowDialog(this) before backgroundWorker.RunWorkerAsync(). So backgroundWorker.RunWorkerAsync() will be called once you close the window.

I think following code should work, as suggested by @Steven Mills also.

private void DoSomeBackgroundStuffWithShowDialog()
{
    ContinuousProgressWindow continuousProgressWindow =
        new ContinuousProgressWindow();
    BackgroundWorker backgroundWorker = new BackgroundWorker();
    backgroundWorker.DoWork += (sender, arguments) =>
    {
        //Do some important stuff for 4 seconds
        Thread.Sleep(4000);
    };
    backgroundWorker.RunWorkerCompleted += (sender, arguments) =>
    {
        //None of the following work for "ShowDialog() method"
        //Ran with debugger - breakpoints not hit!
        continuousProgressWindow.DialogResult = DialogResult.OK;
        continuousProgressWindow.Close();
        continuousProgressWindow.Dispose();
    };

    backgroundWorker.RunWorkerAsync();
    continuousProgressWindow.ShowDialog(this);

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top