【问题标题】:How do I update the progress bar one step, every loop cycle? C#如何在每个循环周期中一步一步更新进度条? C#
【发布时间】:2010-09-16 13:36:03
【问题描述】:

在 C#、windows 窗体中创建一个 .net 应用程序。如何在 100 个循环的每个循环中更新进度条 1 步? (我正在循环中处理一个 excel 表。) 进度条控件位于连接到控制器类的 UI 类中,该控制器类连接到自定义类 (MVC 模式)。循环在自定义类中。 我需要在每个方法中一直发送 UI 类实例还是有更好的方法?

现在进度条会在循环结束后更新。 Application.doevents 和 .update 或 .refresh 不起作用。

【问题讨论】:

  • 在较低级别宣传进度的活动有什么问题?
  • 对不起,我不明白你刚才说什么。进度条是在上级定义的,如果我更新下级的值,上级怎么知道?我必须将 ui 类实例或进度条实例向下传递到较低级别。
  • 我不确定,但如果更新延迟,听起来几乎是线程问题。您是否在使用调用(如果需要)?

标签: c# .net multithreading progress-bar backgroundworker


【解决方案1】:

假设下面是您的班级,负责使用其中的循环进行工作。添加事件以指示您的进度。然后从您的 UI 中简单地处理该事件并相应地更新进度条。

sealed class Looper
{
    public event EventHandler ProgressUpdated;

    private int _progress;
    public int Progress
    {
        get { return _progress; }
        private set
        {
            _progress = value;
            OnProgressUpdated();
        }
    }

    public void DoLoop()
    {
        _progress = 0;
        for (int i = 0; i < 100; ++i)
        {
            Thread.Sleep(100);
            Progress = i;
        }
    }

    private void OnProgressUpdated()
    {
        EventHandler handler = ProgressUpdated;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

您可以通过将 BackgroundWorker 作为 UI 的一部分来实现这一点,在 backgroundWorker.DoWork 事件中您调用 looper.DoLoop()。然后在 looper.ProgressUpdated 事件的处理程序中,您可以调用 backgroundWorker.ReportProgress 从 UI 线程增加进度条。

请注意,将进度本身包含在您的 ProgressUpdated 事件所携带的信息中可能更有意义(我只是不想写一个派生自 EventArgs 的新类来说明这一点;您可能无论如何都要得到照片)。

另外请注意,除非您在与 UI 线程不同的线程上使用循环执行代码,否则上述内容确实没有意义。否则,无论如何,您的所有工作都会在下一次 UI 刷新之前完成,因此您的进度条不会提供任何值(循环完成时它只会从 0 变为 100)。

只是一个如何实现这种事情的例子。

【讨论】:

  • 什么叫公共结构控制私有变量?是封装吗?
  • @hi tech credo: 你是说Progress?这只是一个 .NET 属性,而不是 struct。是的,它是封装的重要部分......听起来这个答案可能有很多对你来说可能是新的。如果您遇到问题或感到困惑,请告诉我,我会尽力提供帮助。
  • 好的,所以我的主线程将运行 UI,并且我使用后台工作程序调用我的控制器方法,然后使用循环调用自定义类中的方法。是否需要在 UI 类中运行循环以重复调用 backgroundWorker.ReportProgress?还是会自动更新?感谢您耐心的回答,我以前从未做过线程。
  • 你的 looper 类应该对 UI 一无所知。您的 UI 代码应将事件处理程序添加到 looper 的 OnProgressUpdated 事件。 @Dan - 感谢您编写代码,我太懒了——呃,我的意思是太忙了——无法自己编写代码。 :)
  • @hi tech 信条:不,您不希望在 UI 线程中出现任何类型的循环。这个想法是处理ProgressUpdated 事件并对其做出反应。要了解有关处理事件的更多信息,请查找有关该主题的好文章,例如:msdn.microsoft.com/en-us/library/aa645739(VS.71).aspx
【解决方案2】:

我通常有一个类在 UI 上进行调用检查。 UI -> "UpdaterClass" -> 其他类。

Updater 类具有预定义的方法和对 UI 控件的引用。所以 Updater.StepProgressBar() 是我调用 UI 进度条的步骤。我将 Updater 类引用传递给任何需要直接更新 UI 的类。

这样,来自不同线程的所有 UI 更新都由一个类处理。不是最通用的方式来暗示它,但它永远不会失败。

示例伪代码:

class Updater()
{

  public ProgressBar pb;

  delegate void UpdateProgressBar();

  public StepProgressBar()
  {
     if(pb.InvokeRequired)
     {
          BeginInvoke(new UpdateProgressBar(this.StepProgressBar);
     }
     else
     {
          pb.Step();
      }
   }

}

类似的东西。

【讨论】:

  • 这对我来说不实用,因为我有许多控制器类可以访问多个 UI,并且每个控制器下都有多个库。
【解决方案3】:

您可以使用委托。当您的后台进程创建自定义类时,请绑定从自定义类调用的委托以报告更新。然后您可以在 UI 层对该调用做出反应并从那里更新进度条。

例如(警告,伪代码):

MyCustomClass class = new MyCustomClass();
class.ProgressUpdated += (s,e)=>{ /* update UI */};
class.StartLoop();

【讨论】:

  • 呃,抱歉之前没有使用过委托,我得先了解一下。
  • @Dan Tao 给出了一个很好的例子来说明如何做到这一点。我建议您在尝试执行此操作之前先对事件和代表进行一些外部阅读。后台处理有其自身的一系列陷阱,您需要知道如何避免这些陷阱,然后再陷入任何过于复杂的问题。
猜你喜欢
  • 1970-01-01
  • 2015-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-26
  • 1970-01-01
  • 2018-07-29
  • 2011-12-08
相关资源
最近更新 更多