C# 后台更新进度条的特定用例
我已经为这个问题寻找了两天多的解决方案,最后决定问这个问题。我发现了许多相关的话题,但似乎没有一个能解决我的问题。最近,我尝试了列出的所有解决方案 背景信息:我有一个类可以处理大量数据的遍历。该类称为Traverse。有一个名为DoFullTraverse(Traverse.DoFullTraverse)的类方法,它运行一次完整的遍历(取决于用户输入)最多需要30秒。我在WPF、MVVM模式中工作。我想更新gui上的状态栏,以了解DoFullTraverse的进度。我在函数的开头计算计算所需的确切循环数,然后增加一个循环计数器。每次它达到另一个1/100,我将进度条增加1。我的进度条(在xaml中)的值绑定到我的VM中名为C# 后台更新进度条的特定用例,c#,wpf,mvvm,backgroundworker,C#,Wpf,Mvvm,Backgroundworker,我已经为这个问题寻找了两天多的解决方案,最后决定问这个问题。我发现了许多相关的话题,但似乎没有一个能解决我的问题。最近,我尝试了列出的所有解决方案 背景信息:我有一个类可以处理大量数据的遍历。该类称为Traverse。有一个名为DoFullTraverse(Traverse.DoFullTraverse)的类方法,它运行一次完整的遍历(取决于用户输入)最多需要30秒。我在WPF、MVVM模式中工作。我想更新gui上的状态栏,以了解DoFullTraverse的进度。我在函数的开头计算计算所需的确
PBarV
的属性
最近的尝试:我尝试了100种不同的解决方案,但我最近的尝试如下所示:
private void runTraverseAndUpdateBar()
{
var worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_Complete);
worker.RunWorkerAsync();
while (!ThreadCheck)
{
Thread.Sleep(500);
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
for (int i = 0; i < 36; i++)
{
Thread.Sleep(500);
PBarV += 3;
}
e.Result = true;
}
void worker_Complete(object sender, RunWorkerCompletedEventArgs e)
{
ThreadCheck = true;
}
private void runTraverseAndUpdateBar()
{
var worker=新的BackgroundWorker();
worker.DoWork+=新的doworkereventhandler(worker\u DoWork);
worker.RunWorkerCompleted+=新的RunWorkerCompletedEventHandler(worker\u Complete);
worker.RunWorkerAsync();
而(!ThreadCheck)
{
睡眠(500);
}
}
无效工作线程(对象发送器,工作线程目标)
{
var worker=发送方作为后台工作人员;
对于(int i=0;i<36;i++)
{
睡眠(500);
PBarV+=3;
}
e、 结果=真;
}
无效工作程序\u完成(对象发送器,RunWorkerCompletedEventArgs e)
{
ThreadCheck=true;
}
我相信我根本上误解了背景工作者的工作方式
主要问题:如果我将函数放入后台工作程序并像往常一样继续,我可以使此方法正常工作。问题是,在我的程序继续之前,我需要来自该函数的数据。因此,我需要它以线性方式执行,但仍然正确地更新状态栏
如果有人能对我所缺少的东西有所了解,甚至能把我推向正确的方向,我将不胜感激
编辑:这不是重复的。您提供的帖子没有涉及线性执行和在继续之前等待后台工作人员完成的问题。
编辑2:(根据@Clemens请求)
在主程序继续之前,我需要背景工作人员完成工作。我正在后台工作程序中运行计算量很大的进程,以便可以更新进度条。但是,在主程序可以继续之前,我需要来自Traverse.DoFullTraverse()的信息代码>
具体来说。主程序应停止所有执行(更新状态栏除外),直到后台工作程序完成Traverse.DoFullTraverse()代码>这里有一个简单的示例,您可以使用它并将其应用于视图模型。使用原型来创建代码并了解其工作原理,以便将其应用于更大、更复杂的应用程序,这一点很重要
请注意,该示例不包括诸如如何实现INotifyPropertyChanged和ICommand之类的琐碎内容——这些都很容易做到
另外,请注意TraverseYo
中的注释。具体来说,就是那些告诉你当前在哪个线程上的线程。理解线程间的执行流对于使其正常工作非常重要。如果您不知道自己在哪个线程上,只需获取当前线程的名称即可。如果是STA,您很可能在UI线程上
public class LongLastingWorkViewModel : INotifyPropertyChanged
{
public bool Busy
{
// INotifyPropertyChanged property implementation omitted
}
public double PercentComplete
{
// INotifyPropertyChanged property implementation omitted
}
public ICommand PerformWork { get; set; }
public LongLastingWorkViewModel()
{
// delegated ICommand implementation omitted--there's TONS of it out there
PerformWork = new DelegatedCommand(TraverseYo);
}
private void TraverseYo()
{
// we are on the UI thread here
Busy = true;
PercentComplete = 0;
Task.Run(() => {
// we are on a background thread here
// this is an example of long lasting work
for(int i = 0; i < 10; i++)
{
Thread.Sleep(10 * 1000); // each step takes 10 seconds
// even though we are on a background thread, bindings
// automatically marshal property updates to the UI thread
// this is NOT TRUE for INotifyCollectionChanged updates!
PercentDone += .1;
}
Busy = false;
});
}
公共类LongLastingWorkViewModel:INotifyPropertyChanged
{
公共图书馆忙
{
//忽略INotifyPropertyChanged属性实现
}
公共双百分比完成
{
//忽略INotifyPropertyChanged属性实现
}
公共ICommand性能工作{get;set;}
公共LongLastingWorkViewModel()
{
//省略了委派的ICommand实现--有很多
PerformWork=新的委托命令(TraverseYo);
}
私有void TraverseYo()
{
//我们在这里的UI线程上
忙=真;
完成百分比=0;
Task.Run(()=>{
//我们在这里的背景线程
//这是一个持久工作的例子
对于(int i=0;i<10;i++)
{
Thread.Sleep(10*1000);//每一步需要10秒
//即使我们在后台线程上,绑定
//自动封送UI线程的属性更新
//对于INotifyCollectionChanged更新,情况并非如此!
完成百分比+=.1;
}
忙=假;
});
}
您可以将Busy
绑定到执行运行时阻止所有UI的覆盖,将PercentComplete
绑定到进度条,并将PerformWork
绑定到按钮。解释“线性执行”以及后台工作人员如何“在继续之前完成”.IMO这没有意义。请确保按预期/要求/适当的间隔调用ReportProgress
。再次编辑。希望您能够理解这一点。仍然没有意义。BackgroundWorker的想法是在主程序完成之前不保留主程序。它并行运行(直到完成)并且可以不时通过其ReportProgress
方法报告进度。如果您不希望主程序继续执行,请不要使用BackgroundWorker。那么,您能否提供一种方法,让我在执行计算繁重的过程时更新进度条,同时也让主程序运行