WPF C# – 从另一个线程更新进度条

我试图从其他类中运行的其他线程更新进度条。 为了解释我的所作所为,我认为一张图片会更好。 我想更新// HERE点的进度条: 在此处输入图像描述

我尝试过使用委托,尝试使用ReportProgress,我认为我基本上试图使用谷歌在前100个结果中报告的所有内容,但没有成功。 我还在学习WPF,这可能是一种愚蠢的方式,我正在寻找一种快速而肮脏的方式来完成工作,但随时告诉我应该为更清洁的应用程序重新设计。

编辑 :更多代码。

在ExecutorWindow.xaml.cs中:

public void RunExecutor() { // CREATE BACKGROUNDWORKER FOR EXECUTOR execBackground.DoWork += new DoWorkEventHandler(execBackground_DoWork); execBackground.RunWorkerCompleted += new RunWorkerCompletedEventHandler(execBackground_RunWorkerCompleted); execBackground.ProgressChanged += new ProgressChangedEventHandler(execBackground_ProgressChanged); execBackground.WorkerReportsProgress = true; execBackground.WorkerSupportsCancellation = true; // RUN BACKGROUNDWORKER execBackground.RunWorkerAsync(); } private void execBackground_DoWork(object sender, DoWorkEventArgs e) { myExecutor = new Executor(arg1, arg2); myExecutor.Run(); } private void execBackground_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MessageBox.Show("RunWorkerCompleted execBackground"); } private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e) { ExecutorProgressBar.Value += 1; } // TESTING private void updateProgressBar(int i) { ExecutorProgressBar.Value += i; } public delegate void callback_updateProgressBar(int i); 

在Executor.cs中:

 public void Run() { string[] options = new string[2]; int i = 0; while (LeftToRun > 0) { if (CurrentRunningThreads  0) { } logfile.Close(); MessageBox.Show("All Tasks finished"); } private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker myBackgroundWorker = sender as BackgroundWorker; string[] options = (string[])e.Argument; string machine = options[0]; string script = options[1]; // UPDATE HERE PROGRESSBAR ? RemoteProcess myRemoteProcess = new RemoteProcess(machine, script); string output = myRemoteProcess.TrueExec(); // UPDATE HERE PROGRESSBAR ? this.logfile.WriteLine(output); } private void backgroundWorkerRemoteProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { CurrentRunningThreads--; } private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e) { //myExecWindow.ExecutorProgressBar.Value = e.ProgressPercentage; // TESTING //ExecutorWindow.callback_updateProgressBar(1); // TESTING } 

编辑2 :我明白了! 事实很简单,但我想我一直在寻找太近了。

在我的ExecutorWindow类中:

 private void execBackground_DoWork(object sender, DoWorkEventArgs e) { myExecutor = new Executor(arg1, arg2); myExecutor.Run(sender); } private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e) { ExecutorProgressBar.Value += 1; } 

在我的Executor课程中:

 private BackgroundWorker myExecutorWindow; [...] public void Run(object sender) { myExecutorWindow = sender as BackgroundWorker; string[] options = new string[2]; int i = 0; while (LeftToRun > 0) { if (CurrentRunningThreads < MaxThreadsRunning) { BackgroundWorker myThread = new BackgroundWorker(); myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork); myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted); myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged); myThread.WorkerReportsProgress = true; myThread.WorkerSupportsCancellation = true; myThread.RunWorkerAsync(new string[2] {opt1, opt2}); CurrentRunningThreads++; i++; LeftToRun--; } } [...] private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker myBackgroundWorker = sender as BackgroundWorker; myBackgroundWorker.ReportProgress(1); // PROCESSING MY STUFF HERE myBackgroundWorker.ReportProgress(1); } private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e) { myExecutorWindow.ReportProgress(1); } 

谢谢 !

您可以使用此基本示例在UI线程上运行任何方法

 this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate() { this.progressBar.Value= 20; // Do all the ui thread updates here })); 

在Dispatcher.Invoke(…)中运行命令,您实际上可以从任何工作线程与UI进行交互,否则您将获得exception。

如果你真的需要对后台线程和主要(UI)线程更新进行最终控制,那么这是一个很棒的教程: http : //blog.decarufel.net/2009/03/good-practice-to-use -dispatcherfunction于wpf.html

您应该能够使用Dispatcher.Invoke方法

例如

  Dispatcher.Invoke( new System.Action(() => myProgressBar.Value = newValue) ); 

我知道了! 事实很简单,但我想我一直在寻找太近了。

在我的ExecutorWindow类中:

 private void execBackground_DoWork(object sender, DoWorkEventArgs e) { myExecutor = new Executor(arg1, arg2); myExecutor.Run(sender); } private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e) { ExecutorProgressBar.Value += 1; } 

在我的Executor课程中:

 private BackgroundWorker myExecutorWindow; [...] public void Run(object sender) { myExecutorWindow = sender as BackgroundWorker; string[] options = new string[2]; int i = 0; while (LeftToRun > 0) { if (CurrentRunningThreads < MaxThreadsRunning) { BackgroundWorker myThread = new BackgroundWorker(); myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork); myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted); myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged); myThread.WorkerReportsProgress = true; myThread.WorkerSupportsCancellation = true; myThread.RunWorkerAsync(new string[2] {opt1, opt2}); CurrentRunningThreads++; i++; LeftToRun--; } } [...] private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker myBackgroundWorker = sender as BackgroundWorker; myBackgroundWorker.ReportProgress(1); // PROCESSING MY STUFF HERE myBackgroundWorker.ReportProgress(1); } private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e) { myExecutorWindow.ReportProgress(1); } 

我找到了一个非常简单的解决方案来创建一个线程来运行任何代码块,并将Invocation处理回主线程以更改控件的属性。 它与.NET 4.5一起开箱即用,并且Dispatcher上的lambda调用可以适用于早期版本的.NET。 主要的好处是当你需要一个快速线程来获得一些非常基本的代码时,它就是如此幸福简单和完美。

因此,假设您在对话框中的某个位置有一个进度条,请执行以下操作:

 progBar.Minimum = 0; progBar.Maximum = theMaxValue; progBar.Value = 0; Dispatcher disp = Dispatcher.CurrentDispatcher; new Thread(() => { // Code executing in other thread while (progBar.Value < theMaxValue) { // Your application logic here // Invoke Main Thread UI updates disp.Invoke( () => { progBar.Value++; } ); } }).Start(); 

您还需要确保您具有对WindowsBase.dll的引用

如果你想要一个更可重用的代码片段作为线程启动运行,你可以使用一个方法作为委托,但我发现内联lambda对于简单的任务来说非常简单,而且你不需要像Background Worker方法一样处理事件。