2013-04-05 79 views
1

我对线程的想法还很陌生,我正在使用BackgroundWorker,试图在进行一些工作时显示进度条。这里的DoWork事件的BackgroundWorker由不同线程拥有的对象

private void BackgroundWorker1DoWork(object sender, DoWorkEventArgs e) 
{ 
    SetDateStartEndTimeForReport(); 

    try 
    { 
      PopulateGrid(); 

    } 
    catch (Exception ex) 
    { 
      Logger.Error(ex.Message); 
      MessageBox.Show(ex.Message); 
      return; 
    } 
} 

现在,我的SetDateStartEndTimeForReport()里,我得到一个InvalidOperationException

private void SetDateStartEndTimeForReport() 
{ 
    Logger.Info("Setting the chosen date and/or start/end times for report..."); 

    // Quite possibly the best yo-yo code I've ever written 
    // "Look, I'm a string. Nope, I'm a DateTime. Tricked you again, I'm a string." 
    var reportDate = Convert.ToDateTime(cmbDateSelecter.SelectedValue.ToString()).ToString("yyyy-MM-dd"); // Exception occurs here 

    Report.Date = reportDate; 

    if (chkboxTimeFrame.IsChecked == true) 
    { 
     Report.StartTime = timeStart.Value.Value.ToString(reportDate + " HH:mm"); 
     Report.EndTime = timeEnd.Value.Value.ToString(reportDate + " HH:mm"); 
    } 

    Logger.Info("Date and/or start/end times set"); 
} 

的异常状态:

调用线程不能访问该对象因为不同的线程拥有它。

所以,我做了一些研究,并找到了约this.Dispatcher.Invoke((Action)(()。现在,当我换,该SetDateStartEndTimeForReport();

this.Dispatcher.Invoke((Action)(() => 
{ 
    SetDateStartEndTimeForReport(); 
})); 

PopulateGrid()抛出同样的异常。现在,我可以将该方法包装在相同的Dispatcher.Invoke()中,这就让问题消失了。不过,我觉得我好像错过了一些更优雅的东西。在函数调用时,任何UI元素都不应该被其他任何东西使用。我的问题是,我可以在相同的Dispatcher.Invoke()中包装两种方法吗?虽然,我真的觉得我不应该在同一个函数中使用其中的两个调用。有没有更好的方法去做我想做的事情?

+0

从另一个线程修改控件的唯一方法是使用Dispatcher.Invoke()。 – 2013-04-05 15:33:56

+0

您不能在工作线程中访问cmbDateSelecter等控件的属性。用worker中需要的值创建一个辅助类,并将其传递给RunWorkerAsync(object)过载。将它从您的DoWork事件处理函数中的e.Argument转换回来。 – 2013-04-05 15:55:07

+0

而不是直接管理您的线程考虑使用基于任务的异步模式(TAP)。这里有一个关于使用TAP的进度报告的简短教程:http://simplygenius.net/Article/AncillaryAsyncProgress – 2013-04-05 16:33:36

回答

3

您可以通过background worker's ProgressChanged event传回数据。这将回传到UI线程。该文档可让您知道您通过致电ReportProgress

请注意,BackgroundWorker's只应在其DoWork中执行业务逻辑。如果你所做的只是更新用户界面,那么你不能在后台线程上执行此操作。如果您需要检索数据,则可以使用后台工作程序将其加载到内存中,并且在完成后,如果已将其挂接(将在UI线程上),将调用OnRunWorkerCompleted事件

如果您想要某种方法来完成它,然后Invoke是唯一的方法。

+0

听起来好像'PopulateGrid'属于已完成事件,而另一部分正在进行中,因此他'你需要利用这两个事件。 – Servy 2013-04-05 15:31:46

+0

我刚刚添加到我的回答:) – 2013-04-05 15:35:13

相关问题