Progress bar never displays

I simply want a nondeterministic progressbar that becomes visible and shows some "activity" to indicate a query is running, and then, when the query is through, goes back to being invisible. Seems simple enough; but with this code:

try
{
    Cursor.Current = Cursors.WaitCursor;
    progressBarChanges.Value = 50;
    progressBarChanges.Step = 20;
    progressBarChanges.Visible = true;
    ... // the meat of the code, where the query is being done, elided
} finally
{
    progressBarChanges.Visible = false;
    Cursor.Current = Cursors.Default;
}

...the progress bar never displays, even though the query takes awhile to run. The progress bar is on a DGV. I realize my progress code is a bit lame, but first things first - I just want the darned thing to show something for starters.

UPDATE

I am setting the progressBar to visible way before I'm calling the BackgroundWorker proc that runs the query:

progressBarChanges.Value = 50;
progressBarChanges.Step = 20;
progressBarChanges.Visible = true;
. . .    
if (args.KeyCode == Keys.Enter)
{
    if (ValidEntryForCRID(textBoxID.Text))
    {
        RetrieveAndBindPlatypusData();
        var tb = (TextBox)Controls.Find("textBoxDuckbill", true).First();
        if (tb != null)
        {
            tb.Focus(); 
        }
        if ((!string.IsNullOrWhiteSpace(textBoxID.Text)) &&
            (backgroundWorkerShowChanges.IsBusy != true))
        {
            backgroundWorkerShowChanges.RunWorkerAsync();
        }
    . . .

...yet the progressBar is never becoming visible (unless I never set it back to visible = false after the DGV's DataSource is assigned the OracleDateTable value of the returned query/result set). If I comment that out, it then (belatedly) finally shows up, apparently thinking (erroneously) "better late than never."

Is there a way to force Windows to "pay attention" to the "progressBarChanges.Visible = true;" line right away? Something like a .ProcessMessages() or this.Refresh or...???

Upvotes: 1

Views: 7409

Answers (2)

This is how I got it to work:

I enable the ProgressBar just before the BackgroundWorker call:

progressBarChanges.Visible = true;
backgroundWorkerShowChanges.RunWorkerAsync();

The BackgroundWorker code gets the OracleDataTable from the DB:

private void backgroundWorkerShowChanges_DoWork(object sender, System.ComponentModel.DoWorkEventArgs args)
{
    holdChanges = GetChangesMade(textBoxID.Text);
}

The BackgroundWorker's "completed" event updates the UI:

private void backgroundWorkerShowChanges_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs args)
{
    PopulateChangesGrid();
}

In PopulateChangesGrid(), after assigning the OracleDataTable object to the DGV, I set the Progress bar invisible:

private void PopulateChangesGrid()
{
    dgvPlatypi.DataSource = holdChanges;
    progressBarChanges.Visible = false;
}

It works like a charm (<-- not necessarily a reference to Windows 8 "Metro" charms).

Upvotes: 0

Justin Pihony
Justin Pihony

Reputation: 67075

This is happening because you are not giving the UI thread time to update. You are updating to visible and immediately running your code. The code should run on a background thread and send update messages.

To elaborate:

When you set the Visible property, the UI thread does not actually update until the end of that current logic. So, what is happening is this:

  • Visible
  • Run process
  • Not Visible
  • Code is complete, so UI is unfrozen and updated...with the last state being Not Visible

What you want is:

  • Visible
  • Kick off new thread to run meat
  • UI Code is complete, so UI is unfrozen and updates the bar to Visible

Then this:

  • ==>New thread runs meat sending updates to the UI thread so that the progress bar can update

Then this:

  • New thread ends and in its final method sets Not Visible
  • UI Code is complete, so UI is unfrozen and updates the bar to Not Visible

A Task or Backgroundworker should be pretty easy to implement this with.

UPDATE FOR CODE TO SHOW CONSTANT SCROLL

I believe you are only missing that the progressbar's style should be ProgressBarStyle.Marquee. If you already are offloading the work to a backgroundworker, then the rest should just work

    public Form1()
    {
        InitializeComponent();
        this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
        this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
        progressBar1.Enabled = progressBar1.Visible = false;
        progressBar1.Style = ProgressBarStyle.Marquee;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        progressBar1.Visible = true;
        progressBar1.Enabled = true;
        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        System.Threading.Thread.Sleep(10000);
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        progressBar1.Visible = progressBar1.Enabled = false;
    }

Upvotes: 7

Related Questions