1

In my C# WinForms app, I have the MainForm in which I am using a BackgroundWorker to read a large Text file.

I am also using a second Form to display a Marquee ProgressBar to inform user that they must wait until file has been completely read.

The problem I am having is the Form with the progressbar (SimpleProgressBar) is frozen until the file is read. Where the BW should be on a separate thread to not let this happen.

I left the code that reads the file as it may be relevant to my problem. However all the code actually works its just that the Form to display the ProgressBar is frozen.

SimpleProgressBar.cs (Form)

//Simple Progress Bar set to Marquee
public partial class SimpleProgressBar : Form
{
    public SimpleProgressBar()
    {
        InitializeComponent();


        //ProgressBar is setup in the designer.
        /*
          System.Windows.Forms.ProgressBar progressBar1;
          this.progressBar1.Location = new System.Drawing.Point(16, 65);
          this.progressBar1.Name = "progressBar1";
          this.progressBar1.Size = new System.Drawing.Size(350, 23);
          this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
          this.progressBar1.TabIndex = 1;
        */

    }
}

MainForm.cs (Form)

    //Class variable
    private SimpleProgressBar wait = new SimpleProgressBar();



    private void generatePreview()
    {
            //Setup BW Thread
            BackgroundWorker worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.WorkerSupportsCancellation = true;
            worker.DoWork += worker_DoWork;
            worker.ProgressChanged += worker_ProgressChanged;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;

            //Start procesing file
            worker.RunWorkerAsync();


            //Show the dialog
            wait.ShowDialog(); //perhaps .Show() would be better ?


    }

    //Once completed put the text into the textbox
    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        textBox1.Text  = ((StringBuilder)e.Result).ToString();

    }

    //Report progress here
    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {


    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bw = (BackgroundWorker)sender;

        int bufferSize = 1024;

        var sb = new StringBuilder();
        var buffer = new Char[bufferSize];
        var length = 0L;
        var totalRead = 0L;
        var count = bufferSize; 

        using (var sr = new StreamReader("c:\200mb_text_file.txt"))
        {
            if (bw.CancellationPending)
            {

            }
            else
            {
                length = sr.BaseStream.Length;
                while (count > 0)
                {
                    count = sr.Read(buffer, 0, bufferSize);
                    sb.Append(buffer, 0, count);

                    totalRead += count;
                }
            }
        }

        e.Result = sb;


    }

UPDATE

So essentially I wanted to create a generic 2nd Form to use a a progress indicator that can be used for multiple purposes.

However it looks like that the BW MUST be on the same thread that hold the UI elements that need updating.

IEnumerable
  • 3,610
  • 14
  • 49
  • 78
  • Where do you update the progress bar? I cannot see the code for it. – techno Feb 17 '14 at 03:40
  • 1
    Is there a particular reason you're using a loop to read into a `StringBuilder` when you could just read the whole file into a string with `string fullText = File.ReadAllText(filename);`? – Jim Mischel Feb 17 '14 at 03:41
  • Yeah, Im reading very large text files so I found a solution here `http://stackoverflow.com/questions/2161895/reading-large-text-files-with-streams-in-c-sharp` – IEnumerable Feb 17 '14 at 03:43
  • Im not updating the ProgressBar because it is set to marquee. As I am not calculating the progress (yet) I thought Marquee would be best. – IEnumerable Feb 17 '14 at 03:44
  • And reading the file into a `StringBuilder` is faster than calling `File.ReadAllText`? Have you actually verified that through testing? – Jim Mischel Feb 17 '14 at 03:46
  • My testing shows minimal difference, there is a lot of difference of opinion. Im trying to just freeup the UI. If the file takes 2 min to load I dont mind. But the UI should not be frozen. – IEnumerable Feb 17 '14 at 03:49

2 Answers2

2

ShowDialog shows a modal dialog that you'll have to explicitly close. When you call ShowDialog, the program will not continue to execute beyond that point. Your background worker's completion event will have to close the dialog.

In addition, you're reading 1,024 bytes at a time and then calling the progress event. Every call to the progress event requires marshaling to the UI thread. It takes approximately zero time to read 1,024 bytes, which means that the progress event is being called continually, which in turn means that the UI thread is nearly 100% occupied with your progress update.

If you really need to report progress as the thing is loading, then use a larger buffer. You'll get better read performance anyway with a 64 Kilobyte buffer. That is:

int bufferSize = 65536;

But your files must be huge. You should be able to read at least 50 megabytes per second unless you have a really slow disk or you're reading over a slow network.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • Yes,Instead use myform.Show() – techno Feb 17 '14 at 03:45
  • Yes, true, I will add this logic in soon. But this is unrelated because the BW is still doing its job. Ad finishes. When the BW is completed the Form with the Progressbar works fine – IEnumerable Feb 17 '14 at 03:45
  • I changed to Show() but still frozen. – IEnumerable Feb 17 '14 at 03:49
  • ok yeah ill try a bigger buffer. But this is still unrelated to why the Form is frozen. Thanks – IEnumerable Feb 17 '14 at 03:55
  • No, that's *completely* related to why the form is frozen. If the UI thread is 100% occupied with servicing updates from your background worker, then the form can't update. The form will appear frozen. – Jim Mischel Feb 17 '14 at 04:05
  • ahh. So even though I am using a BW, are you saying that the BW is completely occupying my PC resources? – IEnumerable Feb 17 '14 at 04:06
  • Thanks Jim, Im going to refactor my code and keep it simple! – IEnumerable Feb 17 '14 at 04:06
  • @IEnumerable: The background worker isn't completely occupying the UI thread. The frequency of updates is doing it. If you don't try to update so frequently, your UI response will be better. – Jim Mischel Feb 17 '14 at 04:10
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/47614/discussion-between-ienumerable-and-jim-mischel) – IEnumerable Feb 17 '14 at 04:14
-1

Progress Bar needs to be updated in another Thread.Updating the progress bar from the UI thread will cause freezing issues. Just put the code to update the progres bar in the Backgroudnworker's DOWork Method rather than reporting it.

 void worker_DoWork(object sender, DoWorkEventArgs e)
    {
    stuffdone()
    progressBar1.PerformStep();

    }

Before using this you will need to set Form.CheckForIllegalCrossThreadCalls = false; so that BW can access the UI

techno
  • 6,100
  • 16
  • 86
  • 192
  • But you can't update the progress bar from a background thread. UI updates have to be done on the UI thread. – Jim Mischel Feb 17 '14 at 03:49
  • Does this mean the BW should be on the Thread/ and Form that the ProgressBar is on ? – IEnumerable Feb 17 '14 at 03:51
  • @IEnumerable Rephrase your question please – techno Feb 17 '14 at 03:52
  • What do you mean "allow it once?" Are you sure it's going to work in release mode when you're not running under the debugger? – Jim Mischel Feb 17 '14 at 03:54
  • @JimMischel Yes,My applications are running that way – techno Feb 17 '14 at 03:54
  • @techno - OK I think Ive probably gone about this the wrong way. Ill put the BW on the 2nd form that has the ProgresBar. – IEnumerable Feb 17 '14 at 03:57
  • @IEnumerable I asked you to rephrase this :) "Does this mean the BW should be on the Thread/ and Form that the ProgressBar is on ?" Did not understand what you are asking here.Not the main question – techno Feb 17 '14 at 04:01
  • 2
    Setting `Form.CheckForIllegalCrossThreadCalls = false` is a *really bad idea*. See http://stackoverflow.com/q/13345091/56778, for example. Even the documentation makes it clear that it's for *debugging only*. – Jim Mischel Feb 17 '14 at 04:03
  • @techno - Its ok, Im starting to see the bigger problem here. Im trying to create a generic Form for feedback to the user so I should just move te BW logic to the second form. This way the BW can access the progressbar as it will be on the same thread. – IEnumerable Feb 17 '14 at 04:04
  • @JimMischel There may be minor issues,only very rarely.My application does not have any problems so far – techno Feb 17 '14 at 04:05
  • @IEnumerable BW will not be on the same thread even if it is written in the same form.The UI Thread is different.As you can see from the previous comment there are may be very minor problems rarely,but you can test for it.I suggest you test my approach and go ahead what you might think is better. Yes,it would be better to put BW code on the same form with the progressbar – techno Feb 17 '14 at 04:10
  • @techno - I know the BW will not be on the same thread but it will be on the same form. This is easy enough to implement I was just hoping to write a simple Generic Progress UI. oh well, learn as we go.. – IEnumerable Feb 17 '14 at 04:13
  • Your experience is different than mine. I have very clear memory of UI updates from background threads causing all kinds of strange, intermittent, and difficult to find bugs. The documentation is very clear that UI updates should be done only on the UI thread, and there are very good reasons for it. You're playing with fire. But it's your code. Knock yourself out. – Jim Mischel Feb 17 '14 at 04:13
  • @JimMischel LOL Yes,this is the case of programs with large number of concurrent UI Updates.But mine was a rather simpler one.As i have said try and do what you find is better in your case. – techno Feb 17 '14 at 04:16