2008-11-25 75 views
4

我正在寻找一种通用的方法来实现长时间操作中的等待屏幕。过去我曾经使用过几次线程,但是我感觉我实施它的要么很差,要么太麻烦(并且复制/粘贴 - 恐怖!)。螺纹加载(等待)屏幕

我想尽可能保持通用和简单,所以我不需要执行加载BackgroundWorker来处理各种废话,使事情难以维护。

这是我想做些什么 - 请注意,这可能会从什么实际上可能相差/最佳实践/不管 - 使用VB.NET,框架2.0(所以没有匿名方法):

Private Sub HandleBtnClick(sender as Object, e as EventArgs) Handles Button.Click 
     LoadingScreen.Show() 

     'Do stuff here, this takes a while!' 
     Dim Result as Object = DoSomethingTakingALongTime(SomeControl.SelectedObject) 

     LoadingScreen.Hide() 

     ProcessResults(Result) 
    End Sub 

该应用程序现在完全是单线程的,因此所有内容都在GUI线程上运行。我需要能够访问DoSomethingTakingALongTime()中的对象而不会出现跨线程异常。 GUI线程等待一些方法(需要很长时间)才能完成,而LoadingScreen表单应该保持响应(它是动画的/有一个进度条/等)。

这是一个可行的/好的方法,还是我看到这种方式太简单了?关于这件事的最佳做法是什么?最重要的是:我怎么能实现这样一个系统?正如我已经提到,我很少有线程的经验,所以请温柔请:-)

回答

4

你的问题是,当你试图将你的工作线程数据传递给你的UI线程时,你得到一个交叉线程异常。你需要做的是检查InvokeRequired和BeginInvoke来对你的UI设置控件之前这样你就不会得到错误,像这样:

Private Sub work_CrossThreadEvent(ByVal sender As Object, ByVal e As System.EventArgs) Handles work.CrossThreadEvent 

     If Me.InvokeRequired Then 
      Me.BeginInvoke(New EventHandler(AddressOf work_CrossThreadEvent), New Object() {sender, e}) 
      Return 
     End If 

     Me.Text = "Cross Thread" 

End Sub 

只是改变New EventHandler部分到事件处理程序的使用。

此外我认为使用后台工作是不是你的工人类不好的方法, 只需创建一个类为您的工作,并使用背景工人做线程的东西有点像这样:

Public MustInherit Class Worker 

    Protected WithEvents worker As BackgroundWorker 

    Public Sub New() 

     worker = New BackgroundWorker() 
     worker.WorkerReportsProgress = True 
     worker.WorkerSupportsCancellation = True 

    End Sub 

    Public Sub Start() 

     If (Not worker.IsBusy AndAlso Not worker.CancellationPending) Then 
      worker.RunWorkerAsync() 
     End If 

    End Sub 

    Public Sub Cancel() 
     If (worker.IsBusy AndAlso Not worker.CancellationPending) Then 
      worker.CancelAsync() 
     End If 
    End Sub 

    Protected MustOverride Sub Work() 

    Private Sub OnDoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork 
     Work() 
    End Sub 

    Public Event WorkCompelted As RunWorkerCompletedEventHandler 
    Private Sub OnRunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted 
     OnRunWorkerCompleted(e) 
    End Sub 
    Protected Overridable Sub OnRunWorkerCompleted(ByVal e As RunWorkerCompletedEventArgs) 
     RaiseEvent WorkCompelted(Me, e) 
    End Sub 

    Public Event ProgressChanged As ProgressChangedEventHandler 
    Private Sub OnProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles worker.ProgressChanged 
     OnProgressChanged(e) 
    End Sub 
    Protected Overridable Sub OnProgressChanged(ByVal e As ProgressChangedEventArgs) 
     RaiseEvent ProgressChanged(Me, e) 
    End Sub 

End Class 

Public Class ActualWork 
    Inherits Worker 

    Public Event CrossThreadEvent As EventHandler 

    Protected Overrides Sub Work() 

     'do work here' 
     WorkABit() 
     worker.ReportProgress(25) 

     WorkABit() 
     worker.ReportProgress(50) 

     WorkABit() 
     worker.ReportProgress(75) 

     WorkABit() 
     worker.ReportProgress(100) 

    End Sub 

    Private Sub WorkABit() 

     If worker.CancellationPending Then Return 
     Thread.Sleep(1000) 
     RaiseEvent CrossThreadEvent(Me, EventArgs.Empty) 

    End Sub 

End Class 

免责声明..与vb有点生疏,但你应该明白了。

0

在你的线程中,使用Application.Run(yourform)来得到你想要的。

请注意,您需要以某种方式指示表单关闭自己。

+0

哪个线程?该应用程序构建为单线程(= GUI线程),它已经使用Application.Run来显示主窗体。 – 2008-11-25 11:06:39

0

我希望你不会觉得这无益 - 但我会问你为什么你想要一个线程等待屏幕?首先使用线程的原因是,UI保持响应,并且长时间的操作在后台完成。否则,你可能只需要在你的FormLoading控件上有一个ProgressBar,并且有DoSomethingTakingALongTime来定期更新它。这根本不需要线程。

+0

对于何时以及如何更新GUI,.NET非常挑剔。有时它运行良好,直到你点击某处,并完全停止更新。我知道长操作应该在线程而不是GUI中执行,但我没有提到它,因为我想知道这个解决方案是否可行。 – 2008-11-25 10:55:39

+0

我真的不关心主窗体,只有加载屏幕应该是响应。我也不关心在哪个线程逻辑执行,但我似乎无法得到它的权利,我总是遇到跨线程异常... – 2008-11-25 10:57:47