Search code examples
c#multithreadingbackgroundworker

C# BackgroundWorker and ProgressBar issue


I have two files. One which contains a variable of type DataProgressBar and calls the startAsyncWorker() method before loading its own components. The other is the DataProgressBar displayed below. When I run my program and call startAsyncWorker() in the other files the behavior

EXPECTED: small window with a progressbar is displayed, loading from 0 to 100 as the work in WorkerDatabaseInsertion is performed. Then when finished my first class file which contains the DataProgressBar, will move on to its next instruction.

EXPERIENCED: small window with a progressbar is displayed, no change is made to the UI, thread seems to freeze as no output or evidence of processing is shown. I close the window, the calling file resumes.

public partial class DataProgressBar : Window
    {
        private BackgroundWorker bgw;
        private String _path;
        private LFPReader _lfp;
        private Access db;


            public DataProgressBar(String p, LFPReader reader, Access database)
            {
                InitializeComponent();
                /* Set private class level variables */
                _path = p;
                _lfp = reader;
                db = database;
                db.open();
                /* Set up worker and progressbar */
                bgw = new BackgroundWorker();
                SetUpWorker(bgw);
                progressbar.Maximum = 100;
                progressbar.Minimum = 0;
                progressbar.Value = 0;
            }

            public void startAsyncWorker()
            {
                if(bgw.IsBusy != true)
                {
                    bgw.RunWorkerAsync();
                }
            }

            /// <summary>
            /// This methods exists for completeness, but we will
            /// probably not need to directly cancel the worker from here.
            /// --Kurtpr
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            public void cancelAsyncWorker(object sender, EventArgs e)
            {
                if (bgw.WorkerSupportsCancellation == true)
                {
                    bgw.CancelAsync();
                }
            }

            private void SetUpWorker(BackgroundWorker worker)
            {
                worker.WorkerReportsProgress = true; // we need this in order to update the UI
                worker.WorkerSupportsCancellation = true; // Not sure why, but we may need this to cancel

                worker.DoWork += new DoWorkEventHandler(WorkerDatabaseInsertion);
                worker.ProgressChanged += new ProgressChangedEventHandler(WorkerProgressChanged);
                worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerBurnNotice);
            }

            private void WorkerDatabaseInsertion(object sender, DoWorkEventArgs e) {
                BackgroundWorker worker = sender as BackgroundWorker;
                ImageInfo ImageData = new ImageDB.ImageInfo();
                double size = _lfp.GetImageData().ToArray().Length;
                int index = 0;
                //_path is setup in loadAsLFP() before this call.
                foreach (var image in _lfp.GetImageData())
                {
                    index++;
                    if (worker.CancellationPending == true)
                    {
                        e.Cancel = true;
                        break;
                    }
                    image.FullPath = Path.Combine(_path, image.FullPath);
                    ImageData.Add(new ImageDB(image));
                    db.insertImage(new ImageDB(image));
                    worker.ReportProgress((int)(index/size));
                }
            }

            private void WorkerProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                Console.WriteLine("Hello");
                progressbar.Value = e.ProgressPercentage;
            }

            private void WorkerBurnNotice(object sender, RunWorkerCompletedEventArgs e)
            {
                db.close();
            }
        }

I suspect I am wrong about a fundamental aspect of BackgroundWorker. Where is my logical fault here?

EDIT Here is the code that calls and creates the DataProgressBar object.

private void createDb(string filePath, LFPReader lfp)
        {
            //Set up LFP related objects.
            ImageData = new ImageDB.ImageInfo();

            //create database file.
            filePath = filePath.Replace(".lfp", ".mdb").Replace(".LFP", ".mdb");
            _lfpName = filePath; // update to use the database file
            Access db = new Access(filePath.Replace(".lfp", ".mdb"));
            db.createDatabase();

            //used for calculating progress of creating this new database.
            var progressbar = new DataProgressBar(_path, lfp, db);
            progressbar.ShowDialog();
            progressbar.startAsyncWorker();

            CurImage = ImageData.First().FullPath;
            //Make sure that data context is set for these images.
            DataContext = ImageData;

        }

Solution

  • I think your progress calculation logic is faulty here.

    worker.ReportProgress((int)(index/size));

    This line will always report progress as 0. So, your progress bar will be always stuck at 0 position.

    Instead, use following to report progress in percentage.

    worker.ReportProgress((int)(index*100/size));

    Update: The code you have shared seems to be correct. I think the problem lies in the way you have implemented progress bar.

    I suppose you are calling startAsyncWorker method from main thread as follows;

    var dpb = new DataProgressBar(p, reader, database);
    dpb.startAsyncWorker();
    

    After above two lines are called, your main thread should be free.

    That means, following code will cause your progressbar to freeze for 50 seconds because, even if your DoWork is running perfectly, UI will not be updated since main thread is not free.

    var dpb = new DataProgressBar(p, reader, database);
    dpb.startAsyncWorker();
    Thread.Sleep(50000);               //Main thread is busy for 50 seconds
    

    Update 2:

    The real problem lies in following lines;

    var progressbar = new DataProgressBar(_path, lfp, db);
    progressbar.ShowDialog();
    progressbar.startAsyncWorker();
    

    Actually ShowDialog() method shows DataProgressBar as Modal dialog. That means, the control will not go to next line unless you close that dialog.

    Your problem should be solved using following code;

    var progressbar = new DataProgressBar(_path, lfp, db);
    progressbar.startAsyncWorker();
    progressbar.ShowDialog();
    

    It will first start your background worker and then DataProgressBar dialog will be shown.