Winforms 进度条不更新 (C#)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1068720/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:28:31  来源:igfitidea点击:

Winforms Progress bar Does Not Update (C#)

c#

提问by

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

在我的程序中 [C# + winforms]。我有进度条和列表视图。

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 %

通过一种方法,我正在执行一些操作,然后在 Listview 中更新数据。添加的记录数是我为 ProgressBar.value 属性设置的值。我想要的是,根据进度条的值,它应该显示其进度。但是进度条没有更新。只有在方法执行进度条结束时才显示整个进度,即 100%

Can someone help me in this regard?

有人可以在这方面帮助我吗?

Thanks, Amit

谢谢,阿米特

回答by Marc Gravell

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

听起来您正在阻塞 UI 线程 - 即您还没有释放系统来进行任何绘制。

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.

一个hacky的答案是注入Application.DoEvents()你的代码 - 但这是有风险的,并且有重入等问题;它只是有点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.

一个更好的选择可能是做一个处理BackgroundWorker,周期性地切换到UI线程更新的东西(Control.Invoke) -但是,如果要添加大量的资料转移到的,这可能会非常棘手ListView

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

完整示例(尽管您可能希望批量更新 UI - 而不是一次一行):

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());
    }
}

回答by Chris Thompson

The ProgressBar.Value must be between 0 and 100.

ProgressBar.Value 必须介于 0 和 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.Valueproperty.

我的猜测是您的问题是您正在更新 GUI 线程上的 ListView。这意味着您需要Application.DoEvents()在更改ProgressBar.Value属性后调用。

It would be best to run on a BackgroundWorkerand use the ProgressChangedevent to handle the ProgressBarupdate.

最好在BackgroundWorker上运行并使用该ProgressChanged事件来处理ProgressBar更新。

Here's another questionabout the same topic.

这是关于同一主题的另一个问题

回答by Al Crowley

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.

正如 Marc 所说,您要确保分出一个新线程来进行长时间运行的计算。这样,用户界面线程(必须执行所有屏幕更新的线程)可以在您更改完成百分比时重新绘制进度条。

It's important to note that onlythe 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.

需要注意的是,只有UI 线程可以更新界面。所以,一旦你在一个单独的线程上运行,你必须通过一个额外的环来确保你的 UI 更改在 UI 线程上处理。如果您不确定您在哪个线程上运行,您可以检查 InvokeRequired 的值(如果您的类是 System.Windows.Form)以查看您是否真的在 UI 线程中。

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.

要在 UI 线程上处理您的命令,请使用 Control.Invoke() 函数来确保在您正在使用的控件的 UI 线程上处理更新。

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.

在我下面的示例代码中,我创建了一个委托函数类型并提前声明了被调用的函数......我没有使用任何很酷的 C# 3.5 函数来完成它,但我敢打赌你可以使用一个 Lamba 表达式来做同样的事。

 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 pageand the MSDN Control.Invoke manual pagefor some more info.

查看MSDN Control.InvokeRequired 手册页MSDN Control.Invoke 手册页了解更多信息。

回答by Al Crowley

Really Sorry Friends,

真的对不起朋友们,

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

实际上,我正在为 ProgressBar.value 字段赋值,但没有使用 update() 方法。我使用了它,我的问题得到了解决。

Thanks all for your replies

谢谢大家的回复