Winforms Progress bar Does Not Update (C#)

你离开我真会死。 提交于 2019-11-26 16:35:29

问题


In my program [C# + winforms]. I have progress bar & listview.

Through one method i am performing some operations & then updating data in Listview. The no of records added is the value i am setting for ProgressBar.value property. What i want here is, According to value of progress bar, it should show its progress. However the progress bar is not getting updated. Only at the end of method execution progress bar shows entire progress i.e. 100 %

Can someone help me in this regard?

Thanks, Amit


回答1:


It sounds like you are blocking the UI thread - i.e. you haven't released the system to do any painting.

A hacky answer is to inject Application.DoEvents() into your code - but this is risky, and has problems with re-entrancy etc; and it is just a bit hacky.

A better option may be to do the processing on a BackgroundWorker, periodically switching to the UI thread to update things (Control.Invoke) - but this may be tricky if you are adding lots of items to a ListView.

Full example (although you might want to batch the UI updates - not a row at a time):

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

class MyForm : Form
{
    BackgroundWorker worker;
    ListView list;
    Button btn;
    ProgressBar bar;
    public MyForm()
    {
        Text = "Loader";
        worker = new BackgroundWorker();
        worker.WorkerReportsProgress = true;
        worker.ProgressChanged += worker_ProgressChanged;
        worker.DoWork += worker_DoWork;
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        list = new ListView();
        list.Dock = DockStyle.Fill;
        Controls.Add(list);
        btn = new Button();
        btn.Text = "Load";
        btn.Dock = DockStyle.Bottom;
        Controls.Add(btn);
        btn.Click += btn_Click;
        bar = new ProgressBar();
        bar.Dock = DockStyle.Top;
        Controls.Add(bar);
    }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        btn.Enabled = true;
    }

    void btn_Click(object sender, EventArgs e)
    {
        worker.RunWorkerAsync();
        btn.Enabled = false;
    }


    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 0; i < 100; i++)
        {
            string newRow = "Row " + i.ToString();
            worker.ReportProgress(i, newRow);
            Thread.Sleep(100);
        }
    }

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        list.Items.Add((string)e.UserState);
        bar.Value = e.ProgressPercentage;
    }

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new MyForm());
    }
}



回答2:


Really Sorry Friends,

Actually, I was assiging value to ProgressBar.value field but didnt use update() method. I used that & my problem got resolved.

Thanks all for your replies




回答3:


As Marc said, you want to make sure that you spin off a new thread to do your long running computation. That way the User Interface thread (which is the one that has to do all the screen updates) can redraw the progres bar whenever you change the percent complete.

It's important to note that only the UI thread can update the interface. So, once you are running on a separate thread, you have to go through an extra hoop to make sure that your UI change is processed on the UI thread. If you aren't sure what thread you are running on, you can check the value of InvokeRequired (if your class is a System.Windows.Form) to see if you are actualy in the UI thread.

To get your command processed on the UI thread, use the Control.Invoke() function to make sure the update is processed on the UI thread for the control you are working with.

In my sample code below I'm creating a delegate function type and declaring the invoked function in advance....I've not done it with any of the cool C# 3.5 functions, but I bet you could work up a lamba expression to do the same thing.

 private void bCreateInvoices_Click(object sender, EventArgs e)
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += new DoWorkEventHandler(CreateInvoices);
        worker.RunWorkerAsync(this);
    }

 // Here is the long running function that needs to update the progress bar
 public void CreateInvoices(object sernder, DoWorkEventArgs e)
    {
        int totalChecked = CountCheckedServiceOrders();
        int totalCompleted = 0;

        foreach (...data to process...) {
            totalCompleted++;
            if (InvokeRequired) {
               Invoke(new Change(OnChange), "status text", 
                        totalCompleted, totalChecked);                
            }
        }
    }

    // this code updates the status while a background thread works
    private delegate void Change(string status, int complete, int total);
    private void OnChange(string status, int complete, int total)
    {
        if (status == null) {
            progressBar.Visible = false;
            lStatus.Text = "Task complete";
            progressBar.Value = 0;
        } else {
            progressBar.Visible = true;
            progressBar.Minimum = 0;
            progressBar.Maximum = total;
            progressBar.Value = complete;
            lStatus.Text = status;
        }

    }

Take a look at the MSDN Control.InvokeRequired manual page and the MSDN Control.Invoke manual page for some more info.




回答4:


The ProgressBar.Value must be between 0 and 100.

My guess is that your problem is that you're updating the ListView on the GUI thread. That means you'll need to call Application.DoEvents() after changing the ProgressBar.Value property.

It would be best to run on a BackgroundWorker and use the ProgressChanged event to handle the ProgressBar update.

Here's another question about the same topic.



来源:https://stackoverflow.com/questions/1068720/winforms-progress-bar-does-not-update-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!