我正在开发一个WPF .NET 3.5应用程序,该应用程序执行一些长任务,我想创建一个单独的线程到UI线程来处理数据,然后在完成时更新UI中的一些标签。我遇到的问题是我使用的函数使用了两个参数,我正在努力解决如何在线程中调用具有多个参数的函数并更新UI。
我一直在玩使用Delegate Sub来调用该函数(它位于一个单独的Class中),我的代码也试图从函数中返回一个数据集,用于调用线程来更新UI,但是我不确定这是否是达到此目的的最佳实践,或者我应该使用调度函数来执行UI更新(反馈将非常感激)。
我的代码如下。
Private Delegate Sub WorkHandler(ByVal input1 As String, ByVal input2 As String)
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim test_helper As New test_global
Dim worker As New WorkHandler(AddressOf test_helper.getWeatherData)
worker.BeginInvoke("IDA00005.dat", "Adelaide", AddressOf weatherCallBack, Nothing)
' The following is what I was using prior to attempting to work with threads, do I continue to update the UI here getting the called function to return a dataset, or do I have the called function do the UI updating?
'Dim ls As DataSet = test_helper.getWeatherData("IDA00005.dat", "Adelaide")
'Dim f_date As String = ls.Tables("weather").Rows(1).Item(3).ToString
End Sub
Public Sub weatherCallBack(ByVal ia As IAsyncResult)
CType(CType(ia, Runtime.Remoting.Messaging.AsyncResult).AsyncDelegate, WorkHandler).EndInvoke(ia)
End Sub
我试图打电话的功能如下:
Class test_global
Public Sub getWeatherData(ByVal filename As String, ByVal location As String) 'As DataSet
...
End Sub
End Class
我的问题是,如果我要调用线程来更新UI,如何让被调用的线程返回数据集,或者如果调用的线程要更新UI,我该如何实现这一点?
更新:
在提供的建议之后,我实现了一个BackgroundWorker,它分别引发DoWork和RunWorkerCompleted事件以获取数据并更新UI。我的更新代码如下:
Class Weather_test
Implements INotifyPropertyChanged
Private WithEvents worker As System.ComponentModel.BackgroundWorker
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim test_helper As New test_global
Dim worker = New System.ComponentModel.BackgroundWorker
worker.WorkerReportsProgress = True
worker.WorkerSupportsCancellation = True
Dim str() = New String() {"IDA00005.dat", "Adelaide"}
Try
worker.RunWorkerAsync(str)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub worker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork
Dim form_Helpder As New test_global
Dim ds As DataSet = form_Helpder.getWeatherData(e.Argument(0), e.Argument(1))
e.Result = ds
End Sub
Private Sub worker_Completed(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
If e.Error IsNot Nothing Then
MsgBox(e.Error.Message)
Else
...
NotifyPropertyChanged("lbl_minToday")
...
End If
End Sub
End Class
然后我在一个单独的类中获得了获取和处理数据的函数。
我能够在Visual Studio 2010中调试代码并显示表单,但标签没有更新,当我在RunWorkerAsync行放置一个断点时,调用该行并且Window_Loaded子句完成但是看起来没有调用DoWork或RunWorkerCompleted事件(至少函数不是)。
任何人都可以提供一些协助,帮助我调试代码,看看为什么不调用这些函数?
此外,上述代码是否是答案中推荐的正确方法?
非常感谢所提供的任何帮助。
马特
答案 0 :(得分:2)
您应该使用BackgroundWorker component。
您应该在DoWork
处理程序中调用您的函数,并将e.Result
设置为返回的DataSet。
然后,您可以在RunWorkerCompleted
处理程序中更新UI。
答案 1 :(得分:1)
我对BackgroundWorker
没有多少经验(我只使用过一次),但它绝对是您问题的解决方案。但是,我总是使用的方法是启动一个新的Thread(而不是通过委托的ThreadPool线程),它获取一个锁,然后更新所有属性。如果您的类实现了INotifyPropertyChanged
,那么您可以使用数据绑定让GUI在属性更改时自动更新。我用这种方法取得了很好的效果。
至于将Dispatcher传递给你的帖子,我相信你也可以这样做。但是,我会轻描淡写,因为我相信我遇到过这种情况,我认为我正在使用的Dispatcher不再与主线程相关联。我有一个库需要调用一个触及GUI元素的方法(即使对话框可能没有显示),我通过使用Dispatcher.Invoke解决了这个问题。我能够保证我正在使用与主线程关联的Dispatcher,因为我的应用程序使用MEF来导出它。
如果您想了解我发布的任何内容的更多详情,请发表评论,我会尽力修饰这些主题。
答案 2 :(得分:1)
使用BackgroundWorker
。实现长时间运行的方法,并将参数传递给DoWorkEventArgs
事件处理程序的DoWork
参数中的方法。请勿在此方法中直接或间接更新UI(即不要更新视图模型的属性)。
使用进度报告在方法运行时更新UI:在长时间运行的方法中调用ReportProgress
,将需要在UI中显示的任何信息传递到UserState
参数中。在ProgressChanged
事件处理程序中,从ProgressChangedEventArgs
获取状态并更新UI(希望通过更新视图模型的相应属性并引发PropertyChanged
)。
您需要实现一个类来包含进度报告的用户状态,因为UserState
的类型为object
。
请注意,您还可以在完成后使用长时间运行方法的结果更新UI。这与进度报告的方式类似:实现一个类来包含结果,将Result
的{{1}}属性设置为此类的实例,结果将在{引发DoWorkEventArgs
事件时{1}}的{1}}属性。
通过检查Result
的{{1}}属性,确保处理长时间运行方法引发的任何异常。