2011-04-26 34 views
14

我被困在试图更新从其他线程运行在不同类中的进度条。为了解释我所做的事情,我认为一张照片会更好。我想更新// HERE点中的进度条:enter image description hereWPF C# - 从另一个线程更新进度条

我试过使用委托,尝试使用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 < 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}); 

      // HERE ? 
      CurrentRunningThreads++; 
      i++; 
      LeftToRun--; 

     } 
    } 

    while (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; 
} 

在我的遗嘱执行人类:

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); 
     } 

谢谢!

+0

优秀的描述,+1。你是手动绘制的还是借助一些代码 - 绘图工具? – 2011-04-26 12:02:36

+0

糟糕的描述。画中画代码不能缩放或复制/粘贴。代码也看起来不完整(在关键点)。 DoWork在哪里? – 2011-04-26 12:05:24

+0

@Teoman Soygul:我是用Visio做的。 @Henk Holterman:当然,我可以使用DoWork代码和其他功能进行编辑,但我并不认为它与这个主题有关,但这就是为什么我没有这样做。 – 2011-04-26 12:27:16

回答

0

我明白了!事实上很简单,但我想我一直在寻找太近以找出答案。

在我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; 
} 

在我的遗嘱执行人类:

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); 
     } 
10

你可以用这个非常基本的样本

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

运行Dispatcher.Invoke(...)内的命令运行在UI线程上的任何方法,你其实可以与UI任何辅助线程交互,否则你会得到一个例外。

如果你真的需要有背景的最终控制线程&主(UI)线程的更新,这里是一个梦幻般的教程:http://blog.decarufel.net/2009/03/good-practice-to-use-dispatcher-in-wpf.html

+0

这将是正常(池)线程的解决方案。 Bgw对进展有特别的支持(避免这种调用模式)。 – 2011-04-26 12:07:26

1

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

eg

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

我发现了一个非常简单的解决方案来创建一个线程来运行任何代码块以及手柄调用回到主线程来更改控件的属性。它使用.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方法那样处理事件。