2015-10-17 34 views
1

问题很简单:我需要在处理耗时计算时更新WPF应用程序的进度。在我的尝试中,我做了一些Google搜索,最后基于这个解决方案的第一个代码片段:How to update UI from another thread running in another class。这里是我的代码:C#WPF应用程序执行任务的更新进度

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 

using System.Threading; 

namespace ThreadTest 
{ 
    public class WorkerClass 
    { 
     public int currentIteration; 
     public int maxIterations = 100; 

     public event EventHandler ProgressUpdate; 

     public void Process() 
     { 
      this.currentIteration = 0; 
      while (currentIteration < maxIterations) 
      { 
       if (ProgressUpdate != null) 
        ProgressUpdate(this, new EventArgs()); 

       currentIteration++; 
       Thread.Sleep(100); // some time-consuming activity takes place here 
      } 
     } 
    } 

    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void btnStart_Click(object sender, RoutedEventArgs e) 
     { 
      WorkerClass wItem = new WorkerClass(); 

      wItem.ProgressUpdate += (s, eArg) => { 
       Dispatcher.BeginInvoke((Action)delegate() { txtProgress.Text = wItem.currentIteration.ToString(); }); 
      }; 
      Thread thr = new Thread(new ThreadStart(wItem.Process)); 
      thr.Start(); 

      // MessageBox.Show("Job started..."); 

      while (thr.IsAlive == true) 
      { 
       Thread.Sleep(50); 
      } 

      MessageBox.Show("Job is done!"); 
     } 
    } 
} 

的问题是,如果我用Dispatcher.Invoke,比工作线程(THR)进入WaitSleepJoin状态的第一个周期传球后并没有恢复,因此整个应用程序冻结。我已经使用了几个建议来代替使用Dispatcher.BeginInvoke,但在这种情况下,进度不会更新,直到过程完成工作。我想这个问题与切换线程有关,但不能得到确切的点。

+0

可能重复[如何从另一个线程更新UI](http://stackoverflow.com/questions/9602567/)。 –

+0

@DourHighArch:这个问题清楚地表明OP已经理解如何使用Dispatcher.Invoke()。他有一个不同的(虽然常见)问题。这个问题不是你建议的副本的重复。它可能是其他问题的重复,但我确实看过并找不到一个用于WPF的文件(Winforms有一个数字),并且以易于理解的方式直接解决了这个问题。 –

+0

@DourHighArch,是的。我看到了该线程,并根据解决方案回复的第一个片段编写了我的代码。正如Peter Duniho所说,这个问题更多的是为什么它可能不起作用。我在这个问题上花了两天没有结果,需要某种地方挖掘。 – Vasiliy

回答

1

这是一个典型的“调用死锁”场景。堆栈溢出有一些解决WinForms的问题的现有问题,但我只能在WPF上下文(Deadlock when thread uses dispatcher and the main thread is waiting for thread to finish)中找到一个相关的问题,但那不是完全相同的问题,或者至少没有答案(然而)直接解决你的问题,并且问题和答案本身还不够完善,我觉得你的问题需要一个新的答案。所以&hellip;


基本的问题是,你已经阻止了你的UI线程等待进程完成。你应该从来没有做到这一点。你应该从来没有出于任何原因阻止UI线程。很明显,如果您在UI线程中运行的代码中等待任何原因,那么UI本身无法响应用户输入或执行任何类型的屏幕刷新。

有很多可能的方式来解决这个问题,但处理这一当前成语是使用asyncawait,随着Task运行您的程序。例如:

private async void btnStart_Click(object sender, RoutedEventArgs e) 
{ 
    WorkerClass wItem = new WorkerClass(); 

    wItem.ProgressUpdate += (s, eArg) => { 
     Dispatcher.BeginInvoke((Action)delegate() { txtProgress.Text = wItem.currentIteration.ToString(); }); 
    }; 

    Task task = Task.Run(wItem.Process); 

    // MessageBox.Show("Job started..."); 

    await task; 

    MessageBox.Show("Job is done!"); 
} 

注意的是,虽然上述声明async方法void,这是例外。也就是说,通常应该声明async方法返回TaskTask<T>。例外情况是这种情况,其中方法是一个事件处理程序,因此需要匹配现有的签名,其中必须声明方法以返回void

1

我跑你的代码,因为它是和注释这样的:

while (thr.IsAlive == true) 
      { 
       Thread.Sleep(50); 
      } 

一切工作正常。用户评论/

/编辑得到通知处理完成的,做这些改变:

一个。 public event EventHandler ProgressCompleted;在你的WorkerClass中。

b。

if (ProgressCompleted != null) 
     ProgressCompleted(this, new EventArgs()); 

在Process()方法中完成之后。

c。在创建线程之前在您的BtnStart_Click中。

wItem.ProgressCompleted += (s1, eArgs1) => 
      { 
       MessageBox.Show("Job is done!"); 
      }; 
+0

AnjumSKhan,谢谢你的回复。确实有效,但“工作完成了!”消息出现在按钮被点击后,而不是线程thr完成后。我需要它在thread thr完成它的工作后出现,因为会有一些基于wItem.Process()结果的计算。 – Vasiliy

+0

AnjumSKhan,更新后的版本适合我。但是在Worker类中需要多一个事件,并且如果我需要在“作业已完成”消息的同时实现更复杂的逻辑,则会使代码更加庞大。所以在这种特殊情况下,我选择了基于TPL的解决方案。无论如何,你的想法对于特定的用例很有意义并且适用,我已经将它放在了财政部的想法中。谢谢! – Vasiliy

+0

@Vasiliy计数任何WPF控件中的事件数。 – AnjumSKhan

相关问题