【问题标题】:Winforms Progress bar Does Not Update (C#)Winforms 进度条不更新 (C#)
【发布时间】:2009-07-01 11:52:48
【问题描述】:

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

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

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

谢谢, 阿米特

【问题讨论】:

  • 我对 C# 的了解不够,无法在此回答,但在 Java Swing 中,它需要有一个单独的线程,否则你会得到同样的行为。

标签: c#


【解决方案1】:

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

一个 hacky 的答案是将Application.DoEvents() 注入您的代码 - 但这是有风险的,并且存在重入等问题;而且有点hacky。

更好的选择可能是在 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());
    }
}

【讨论】:

    【解决方案2】:

    真的很抱歉朋友们,

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

    感谢大家的回复

    【讨论】:

      【解决方案3】:

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

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

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

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

       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 manual pageMSDN Control.Invoke manual page 了解更多信息。

      【讨论】:

        【解决方案4】:

        ProgressBar.Value 必须介于 0 和 100 之间。

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

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

        Here's another question关于同一主题。

        【讨论】:

        • 为什么进度条值必须在0到100之间?
        • 可以通过设置progressBar1.Maximum = [int]来增加进度条最大值,其中[int]是一个整数。
        • ""ProgressBar.Value 必须介于 0 和 100 之间。"" 这是大错特错。它的最大值属性类型为 int。所以它可以很容易地分配 int.MaxValue
        猜你喜欢
        • 2013-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多