【问题标题】:Update Progressbar from another class从另一个类更新进度条
【发布时间】:2013-12-18 04:43:36
【问题描述】:

我正在用 C#/WPF (.Net framework 4.0) 编写一个应用程序。我遇到的一件事是如何在应用程序非常繁忙时更新控件,如果可能的话。我想我只是在尝试我在网上找到的东西以使其工作(并且在极少数情况下确实如此)。 该应用程序有四个类,一个主窗口类、一个 Worker1 类和一个 Worker2 类以及一个静态 ExtensionMethods 类。 Worker2 从 Worker1 调用并完成大部分工作。当它运行时,CPU 为 100%(核心为 100%,系统有四个核心)。 (都在同一个命名空间中。)

public static class ExtensionMethods
{
    private static Action EmptyDelegate = delegate() { };

    public static void Refresh(this UIElement uiElement)
    {

        uiElement.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, EmptyDelegate);
    }
}

主窗口调用 Worker1 中的顶级函数,该函数经过一些设置,然后开始漫长的过程,过程如下:

MainWindow mainWindow = App.Current.MainWindow as MainWindow;
mainWindow.dispatcher_UpdateProgressBarMaximum(maxValue);
int count=0;
Worker2 worker2 = new Worker2();
while (bytesRead =(readData() !=0) {
    count++;
    worker2.doWork();
    mainWindow.dispatcher_UpdateProgressBar(count);

}

在mainWindow类中是:

dispatcher_UpdateProgressBar(int Value)
{
Dispatcher.BeginInvoke(new Action(() => myProgressBar.Value = value), null);
        myProgressBar.Refresh();
}

(我也尝试过:myProgressBar.Dispatcher.Begin...等。

但两者都不重要。对于有 2 或 3 次读取的小文件,我可以看到进度条正在更新。但是对于大文件,直到最后都没有更新。 这也发生在我显示一些状态信息的一些标签控件上。完成整个工作后,我可以选择显示已完成工作的消息框。对于标签,当我显示消息框时,标签会更新。该应用程序可以一个接一个地处理许多文件。

我考虑过使用后台线程,但不知道该怎么做。也就是说,worker2.doWork() 部分非常密集,但如果它在它自己的线程上,则 mainWindow.dispatcher_Update... 将在线程开始后立即被调用,并且没有什么可以阻止下一次读取的发生,对吧?

也许按照我的编码方式是不可能的。如果我使用 Backgroundworker,该类会在主窗口或 Worker1 中实例化吗?即使是一两个指针也很棒。

【问题讨论】:

标签: c# wpf backgroundworker


【解决方案1】:

在 .NET 4.5 中,他们添加了 IProgress<T> 接口和 Progress<T> 类,可让您从另一个线程报告进度,并且如果在那里创建了 Progress 对象,它将自动在 UI 线程上运行回调。

由于您使用的是 .NET 4.0,因此您并非完全不走运,Microsoft 通过 NuGet 包 Microsoft.BCL 将这些类作为 Out of band update 发布到框架中,它将这两个类反向移植到 .NET 4.0 .

拥有Progress<T> 后,它就相当容易使用了。

void StartWorker()
{
    var progress = new Progress<int>(UpdateProgressBar);

    worker1.Start(progress)
}


void UpdateProgressBar(int Value)
{
    //This code is invoked on the UI thread
    myProgressBar.Value = value;
}

//Elsewhere in Worker1
void Start(IProgress<int> progress)
{

    int count=0;
    Worker2 worker2 = new Worker2();
    while (bytesRead =(readData() !=0) 
    {
        count++;
        worker2.doWork();
        progress.Report(count);
    }
}

不过,尽管如此,您应该尝试重新考虑您的数据模型。 “拥抱 WPF”并开始使用 MVVM 之类的概念来设置绑定,然后更新数据对象,然后更新 UI(您可以将所有 UI 编组代码放在 ViewModel 中的 OnPropertyChanged 事件中)。

【讨论】:

  • 我非常希望能避免这样的事情。此 UI 更新是/是事后的想法。我会通过 MVVM 或后台工作程序(如下)的修补来完成。大多数情况下,由于应用程序的操作,即在操作期间,UI 不会更新。谢谢!!!顺便说一句(我知道我不应该为此使用 cmets。)我是否注意到到目前为止所有内容都在一个线程上?
  • @Ron 如果您使用未列为答案的解决方案,请发布您自己的答案,解释您所做的事情并将其标记为已接受,这样该问题就不会每隔几个月自动升级为问题没有接受的答案。至于为什么不更新,那是因为到目前为止一切都在一个线程上。您可以使用Tasks 来启动后台工作程序,请注意,如果工作程序运行时间超过几秒钟,您应该指定它们LongRunning
  • 我支持 MVVM 方法,这绝对是要走的路。绑定为您处理所有线程封送处理,将ProgressBar 绑定到模型并让它在您更新属性时自动更新视图变得非常容易。
【解决方案2】:

使用BackgroundWorker 进行后台工作并通过ReportProgress 调用更新您的GUI,并通过服务ProgressChanged event 来处理它,其中包含“进度” - 只需从那里更新您的进度条。

如果您需要在第一个任务完成后启动另一个后台任务,您可以通过RunWorkerCompleted event 执行此操作。

【讨论】:

  • BackgroundWorker 是否在 WPF 中定义?
  • 它在 System.ComponentModel 中,但我建议不要在 WPF 中使用它。现在有更好的方法来处理更新进度。
猜你喜欢
  • 2015-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多