更新progressbar的专用C#类

更新progressbar的专用C#类,c#,wpf,progress-bar,C#,Wpf,Progress Bar,我想创建一个专用类来更新我的应用程序中的进度条(在本例中是WPF进度条)。我是这样做的: public class ProgressBarUpdate : IDisposable { private readonly double _delta; private int _current; private int _total; private readonly ProgressBar _pb; public ProgressBarUpdate(Prog

我想创建一个专用类来更新我的应用程序中的进度条(在本例中是WPF进度条)。我是这样做的:

public class ProgressBarUpdate : IDisposable
{
    private readonly double _delta;
    private int _current;
    private int _total;
    private readonly ProgressBar _pb;

    public ProgressBarUpdate(ProgressBar pb, int total)
    {
        _pb = pb;
        _total = total;
        // the pb.Maximum is a double so it doesn`t get truncated
        _delta = _pb.Maximum / total; 
        _current = 0;
        _pb.Visibility = Visibility.Visible;

    }
    public void Dispose()
    {
        _pb.Visibility = Visibility.Collapsed;
        _current = 0;
    }
    public void UpdateProgress()
    {
        _pb.Value =(int)_delta * (++_current);
    }
我这样使用(在UI线程中):

使用(var pu=new ProgressBarUpdate(pb,totalCount)
{

对于(x=0;x如果您正在UI线程上执行工作并调用UpdateProgress,则在您完成工作并且UI线程可以执行其他工作(如刷新UI)之前,它不会更新。因此,这永远不会起作用

如果您在后台线程上执行工作,则需要使用调度程序将设置值封送到UI线程

这里有一个例子

试试这个:

public ProgressBarUpdate(ProgressBar pb, int total)
{
    _pb = pb;
    _total = total;
    _delta = _pb.MaxValue/((double)total);  /make sure you do not truncate delta
    _current = 0;
    _pb.Visibility = Visibility.Visible;

}
public void Dispose()
{
    _pb.Visibility = Visibility.Collapsed;
    _current = 0;
}
public void UpdateProgress()
{
    _pb.Value = (int)( _delta * (++_current)); //update after the increment
}

我还建议使用
float
而不是
double

Winforms/WPF程序是一个
事件处理系统。有一个线程可以连续处理事件队列中的事件。这是它的主要任务,理想情况下,这是它应该做的唯一事情。任何类型的UI活动都会在事件中生成事件队列-就像您将鼠标移到窗口上,或单击某个窗口或其他窗口与窗口重叠,然后当窗口离开重叠位置时再次重叠。所有这些事件都由UI线程处理,从而使UI始终保持更新

此外,Winforms/WPF只允许在UI线程上访问和/或更新控件及其属性,因此有必要以线程安全的方式访问和/或更新控件及其属性

如果您阻止此UI线程或对其执行其他CPU限制的计算,则您的UI响应和更新行为将受到影响。最坏情况下,UI将冻结

因此,正确的答案是在另一个工作线程上执行计算循环,并且只通过使用
调度程序
封送对UI线程的调用来更新进度条UI

然而,为了回答您的问题并满足您的调查,这里有一些事情是可能的-但这是一种不良做法,您应该永远不要做以下事情…

简单地说,当您更新进度条的
属性时,它会使进度条UI无效-因此,UI必须更新。因此,假设在事件队列中生成了一个事件,该事件将导致一些代码运行,这些代码将更新UI。但是,您在UI线程上以循环方式运行-因此,线程没有机会除非循环结束,否则无法处理此事件。因此,您看不到任何UI更新。诀窍是让UI线程在您对进度条的
值进行下一次更新之前处理该事件。您可以通过强制将优先级较低的项调用到事件队列中来完成此操作,以便处理优先级较高和正常的项在进入下一个迭代之前

using (var pu = new ProgressBarUpdate(pb, totalCount))
{
  for (int x = 0; x < totalCount ; x++)
  {
     // operations here 
     pu.UpdateProgress();

     Dispatcher.Invoke(DispatcherPriority.Background, new Action(()=>{}));
  }
}
使用(var pu=new ProgressBarUpdate(pb,totalCount))
{
对于(int x=0;x{}));
}
}

你一直在说你想避免使用线程,我想是因为你不想不必要的复杂化,但这真的没什么大不了的。让一个操作多线程化是一件非常简单的事情。即使对于非常短和简单的任务,这也是实现你想要的最直接的方法。使用它,它看起来会像例如:

using System.Threading.Tasks;

...

Task.Factory.StartNew(() => {
    for (...) {
        // operation...
        progressBar.Dispatcher.BeginInvoke(() => progressBar.Value = ...);
    }
});

可能是因为您的UI不仅“不更新”,而且还在阻塞?您是否对其进行了调试,以确保
\u pb.Value
UpdateProgress
调用中得到了预期的更改?但这在我的UI中没有正确更新。这意味着什么?您可能正在使用(var pu…blah
在UI线程上,阻止任何绘图发生,直到工作完成。停止阻止UI线程。将长时间运行的任务放在另一个线程上。感谢您的回复,工作在UI线程中完成。我想创建一个通用类,以始终更新主线程中使用的任何progressbar。问题事实上,在循环完成之前,这不会重新绘制UI。这不是很长的工作,我希望有一个进度条指示正在工作。只需几秒钟就可以重新加载一些文件。@Domenicolalisi您对“长时间工作”的定义与计算机对“长时间工作”的定义不同。;)是的,修复了,在WPF版本中,MaxValue被称为Maximum,是一个双精度的,因此不需要重铸。谢谢。谢谢你的完整回复,所以如果我答对了,你添加的代码很好,但通常应该在另一个线程中?是否可以在单独的类中移动线程操作,或者它需要在主UI代码中?Btw如果我尝试这个,我会得到“非静态字段需要一个对象引用”好的,我发现:我把它放在UpdateProgress方法中,它可以工作:
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background,new Action(()=>{}));
我将在我的下一个项目中尝试这个方法。谢谢!
using (var pu = new ProgressBarUpdate(pb, totalCount))
{
  for (int x = 0; x < totalCount ; x++)
  {
     // operations here 
     pu.UpdateProgress();

     Dispatcher.Invoke(DispatcherPriority.Background, new Action(()=>{}));
  }
}
using System.Threading.Tasks;

...

Task.Factory.StartNew(() => {
    for (...) {
        // operation...
        progressBar.Dispatcher.BeginInvoke(() => progressBar.Value = ...);
    }
});