我正在用C#/ WPF(.Net framework 4.0)编写一个应用程序。我遇到问题的一件事是如何在应用程序非常繁忙时更新控件。我想我只是试图尝试在网上找到的东西让它起作用(在极少数情况下它会这样)。 该应用程序有四个类,主窗口类,Worker1类和Worker2类以及静态ExtensionMethods类。从Worker1调用Worker2并完成大部分工作。当它运行时,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中实例化?即使是一两个指针也会很棒。
答案 0 :(得分:3)
在.NET 4.5中,他们添加了IProgress<T>
接口和Progress<T>
类,它允许您报告来自另一个线程的进度,如果Progress
,它将自动在UI线程上运行回调对象是在那里创建的。
由于你在.NET 4.0上运行并不是完全没有运气,微软通过NuGet包Out of band update将这些类作为Microsoft.BCL发布到框架中,后者将这两个类反向移植到到.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
事件中)
答案 1 :(得分:2)
使用BackgroundWorker进行后台工作,并通过ReportProgress来电更新GUI,并通过提供包含“进度”的ProgressChanged event进行处理 - 只需更新进度条即可那里。
如果你需要在第一个任务完成时启动另一个后台任务,你可以从RunWorkerCompleted event完成。