Winforms进度条不更新(C#)

15

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

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

有人能帮助我吗?

谢谢,Amit


2
我对C#的了解不足以回答这个问题,但在Java Swing中,它需要有一个单独的线程,否则你会得到同样的行为。 - Chet
4个回答

28

看起来你正在阻塞UI线程 - 也就是说,你没有释放系统完成任何绘制操作。

一种可行的方法是将 Application.DoEvents() 插入到你的代码中 - 但这很危险,并且存在重入等问题;而且这只是一个不太正规的方法。

更好的选择可能是在 BackgroundWorker 上进行处理,定期切换到UI线程更新内容(Control.Invoke)- 但如果你正在向 ListView 添加大量项目,则可能会比较棘手。

完整示例(尽管你可能想将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());
    }
}

10

非常抱歉朋友们,

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

感谢大家的回复。


3
正如Marc所说,您要确保启动一个新线程来执行长时间运行的计算。这样用户界面线程(负责所有屏幕更新的线程)就可以在您更改完成百分比时重新绘制进度条。
需要注意的是,只有 UI 线程可以更新界面。因此,一旦您在单独的线程上运行,就必须通过额外的步骤来确保您的 UI 更改在 UI 线程上处理。如果您不确定自己在哪个线程上运行,可以检查 InvokeRequired 的值(如果您的类是 System.Windows.Form),以查看您是否实际上在 UI 线程上运行。
为了在 UI 线程上处理命令,请使用 Control.Invoke() 函数,以确保更新在您正在使用的控件的 UI 线程上处理。
在下面的示例代码中,我创建了一个委托函数类型,并预先声明了调用的函数...我没有使用任何很酷的 C# 3.5 函数,但我敢打赌您可以编写一个 lambda 表达式来做同样的事情。
 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;
        }

    }

请查看MSDN Control.InvokeRequired手册页面MSDN Control.Invoke手册页面以获取更多信息。

-3

ProgressBar.Value 必须在 0 和 100 之间。

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

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

这里有另一个关于同一主题的问题。


为什么进度条的值必须在0和100之间? - Hemant
你可以通过设置progressBar1.Maximum = [int]来增加进度条的最大值,其中[int]是一个整数。 - Robert Smith
“ProgressBar.Value 的值必须在 0 到 100 之间。”这是非常错误的。ProgressBar具有int类型的最大值属性,因此可以轻松地将其分配为int.MaxValue。 - Peck_conyon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接