Programmer
Programmer

Reputation: 381

Two issues with backgroundworker with progress bar WPF

I'm using WPF and I have main thread which is GUI (wizard).

When user click Finish on wizard it open second thread which display user progress bar used in background worker.

In Main thread I doing:

MessageWithProgressBar progress = new MessageWithProgressBar();
progress.Show();
createFilesInA();
createFilesInB();
createFilesInC();
createFilesInD();
createFilesInE();
createFilesInF();
createFilesInG();
createFilesInH();
createFilesInI();
createFilesInJ();
createFilesInK();

In each createFiles method I increment by 1 the static variable called currentStep which I used it in background worker as detailed below.

In background worker I doing:

public partial class MessageWithProgressBar : Window
{
    private BackgroundWorker backgroundWorker = new BackgroundWorker();
    public MessageWithProgressBar()
    {
        InitializeComponent();
        backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.ProgressChanged += ProgressChanged;
        backgroundWorker.DoWork += DoWork;
        backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
    }

    private void DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(100);
        int i = GeneralProperties.General.currentStep;
        if (i > GeneralProperties.General.thresholdStep)
        {
            progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                        new DispatcherOperationCallback(delegate
                                        {
                                            progress.Value = 100;
                                            title.Content = progress.Value.ToString();
                                            return null;
                                        }), null);
            return;
        }
        else
        {
            progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                        new DispatcherOperationCallback(delegate
                                        {
                                            progress.Value = (int)Math.Floor((decimal)(8 * i));
                                            progressLabel.Text = progress.Value.ToString();
                                            return null;
                                        }), null);
        }
    }

    private void ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                        new DispatcherOperationCallback(delegate
                                        {
                                            progress.Value = e.ProgressPercentage;
                                            return null;
                                        }), null);
    }

    private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                        new DispatcherOperationCallback(delegate
                                        {
                                            progress.Value = 100;
                                            title.Content = progress.Value.ToString();
                                            return null;
                                        }), null);
        WindowMsgGenDB msg = new WindowMsgGenDB();
        msg.Show();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        if (backgroundWorker.IsBusy == false)
        {
            backgroundWorker.RunWorkerAsync();
        }
    }
}

The main thread updated variable called currentStep and the second thread used it to report on the main thread progress.

The operations of the main thread takes a few seconds (not more 15 seconds)

I have two issues:

  1. I see on progress bar only when currentStep=2 (then the progress is 16) and then the progress is 100, and I don't see every step

  2. At the beginning, the progress bar is freeze and it seems like it stuck.

(maybe it connects to the call progress.Show() from the main thread?)

Thanks!

Upvotes: 1

Views: 1763

Answers (1)

Thorsten Dittmar
Thorsten Dittmar

Reputation: 56697

As far as I understand your code your background worker is not doing anything, really. It updates the progress once and that's it.

Also: using global static variables to communicate between a form and a background worker - ouch...

Also, you're using it wrong in my opinion. The work (CreateFilesInA ... CreateFilesInK) should be done by the background worker - that's what it is for. As the main thread will be blocked the way you implemented it, you will not see any updates otherwise.

The usual way to implement something like this is:

  1. Create progress window and disable UI
  2. Start background worker that does stuff in DoWork. In DoWork, after every call to a CreateFilesInXYZ method, call ReportProgress to the the UI be updated.
  3. Update stuff in progress window whenever ProgressChanged event is fired
  4. Hide progress window and enable your application's UI when background worker is done

The way you're doing it it's in no way asynchronous. So, actually, your code should look something like this:

public partial class MainWindow : Window
{
    private BackgroundWorker backgroundWorker = new BackgroundWorker();
    private MessageWithProgressBar progressWindow;

    public MainWindow()
    {
        InitializeComponent();
        backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.ProgressChanged += ProgressChanged;
        backgroundWorker.DoWork += DoWork;
        backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        progressWindow = new MessageWithProgressBar();
        progressWindow.Owner = this;
        progressWindow.Show();

        backgroundWorker.RunWorkerAsync();
    }


    private void DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = (BackgroundWorker)sender;
        int numSteps = 11;
        int currentStep = 0;
        int progress = 0;

        CreateFilesInA();
        currentStep += 1;

        progress = (int)((float)currentStep / (float)numSteps * 100.0);
        worker.ReportProgress(progress);

        CreateFilesInB();
        currentStep += 1;

        progress = (int)((float)currentStep / (float)numSteps * 100.0);
        worker.ReportProgress(progress);

        // All other steps here
        ...
    }

    private void ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressWindow.progress.Value = e.ProgressPercentage;
    }

    private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        progressWindow.Close();

        WindowMsgGenDB msg = new WindowMsgGenDB();
        msg.Show();
    }
}

Please note that the above code goes into your main window! The MessageWithProgressWindow does not contain any code. Maybe the Window_Loaded event handler is not the right place to start the background worker, but you get the picture.

Upvotes: 2

Related Questions