2013-12-17 66 views
2

我在C#/ WPF(.Net框架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); 

} 

在主窗口类为:

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中实例化?即使是一个或两个指针也会很棒。

+1

我编辑了你的标题。请参阅:“[应该在其标题中包含”标签“](http://meta.stackexchange.com/questions/19190/)”,其中的共识是“不,他们不应该”。 –

回答

2

使用BackgroundWorker作为您的后台工作,并通过ReportProgress调用更新GUI,并通过服务ProgressChanged event(包含“进度”)处理它 - 只需从那里更新进度栏即可。

如果您需要在第一个后台任务完成时启动另一个后台任务,您可以从RunWorkerCompleted event开始。

+0

是[WPF](http://stackoverflow.com/tags/wpf/info)中定义的[BackgroundWorker](http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx) ? – jp2code

+0

它在'System.ComponentModel'中,但我建议不要在WPF中使用它。现在有更好的方法来处理更新进度。 – Erik

3

在.NET 4.5,他们增加了IProgress<T>接口和Progress<T>类,可以让你从另一个线程报告进度,如果Progress创建对象有它会自动在UI线程上运行回调。

因为你在.NET 4.0上并不完全不幸,所以Microsoft通过NuGet Package 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事件中)。

+0

我很希望避免这样的事情。这个UI更新是事后才想到的。我会这样做,无论是通过MVVM还是后台工作者修补(下图)。大多数用户界面不会因应用程序的操作而更新,即在操作过程中。谢谢!!!顺便说一下(我知道我不应该为此使用注释)。我注意到现在的一切都在一个线程上? – Ron

+0

@Ron如果您使用未列为答案的解决方案,请发布自己的答案,解释您做了什么并将其标​​记为已接受,以便每隔几个月不会自动提升问题作为没有被接受答案的问题。至于为什么它没有更新,那是因为一切到现在都在一个线程上。您可以使用'任务'启动后台工作,请注意,如果工作人员需要超过几秒的时间运行,您应该指定他们['LongRunning'](http://msdn.microsoft.com/zh-cn/我们/库/ system.threading.tasks.taskcreationoptions%28v = vs.110%29.aspx) –

+0

我第二MVVM的方法,这绝对是要走的路。绑定可以处理所有的线程编组,您可以很容易地将ProgressBar绑定到模型,并在更新属性时自动更新视图。 – Erik