.net 从其他线程打开Windows窗体时未关闭
我创建了一个简单的窗口窗体(粘贴在下面)。现在,当运行一个耗费时间的复杂操作时,我使用它向用户显示wait dailog 调用.net 从其他线程打开Windows窗体时未关闭,.net,vb.net,winforms,devexpress,.net-2.0,.net,Vb.net,Winforms,Devexpress,.net 2.0,我创建了一个简单的窗口窗体(粘贴在下面)。现在,当运行一个耗费时间的复杂操作时,我使用它向用户显示wait dailog 调用HerculesWaitForm.Show(“一些标题”、“一些标题…)会像往常一样显示表单,但调用HerculesWaitForm.Close时仍会显示表单。我可以做些什么来克服此问题 Imports System.Threading Public Class HerculesWaitForm Inherits DevExpress.Utils.WaitDial
HerculesWaitForm.Show(“一些标题”、“一些标题…)
会像往常一样显示表单,但调用HerculesWaitForm.Close
时仍会显示表单。我可以做些什么来克服此问题
Imports System.Threading
Public Class HerculesWaitForm
Inherits DevExpress.Utils.WaitDialogForm
Private Shared form As HerculesWaitForm
Private Shared _thread As Thread
Private Shared _caption As String, _title As String
Private Sub New(ByVal caption As String, ByVal title As String)
MyBase.New(caption, title)
End Sub
Private Shared Sub CreateInstance()
form = New HerculesWaitForm(_caption, _title)
Application.Run(form)
Application.DoEvents()
End Sub
Public Overloads Shared Sub Show(ByVal caption As String, ByVal title As String)
_caption = caption
_title = title
If form Is Nothing Then
_thread = New Thread(AddressOf CreateInstance)
_thread.IsBackground = True
_thread.Start()
End If
End Sub
Public Overloads Shared Sub SetCaption(ByVal caption As String)
If form IsNot Nothing Then
_caption = caption
form.SetFormCaption()
Else
Show(caption, "")
End If
End Sub
Public Overloads Shared Sub Close()
If form IsNot Nothing Then
form.CloseForm()
form = Nothing
End If
End Sub
Private Sub CloseForm()
If Me.InvokeRequired Then
Invoke(New MethodInvoker(AddressOf CloseForm))
Return
End If
Application.ExitThread()
End Sub
Private Sub SetFormCaption()
If Me.InvokeRequired Then
Invoke(New MethodInvoker(AddressOf SetFormCaption))
Return
End If
MyBase.SetCaption(_caption)
End Sub
End Class
如果您将其称为:
Private Sub doSomethingLong()
HerculesWaitForm.Show("hi", "there")
Sleep(someAmount) 'Random long operation '
HerculesWaitForm.Close()
End Sub
然后,正如您所注意到的,如果打开和关闭调用之间的时间很短,您可能会遇到问题。这是因为对Show()的调用
在执行操作时,启动第二个线程创建新的hercules对象。长操作立即开始,同时线程开始启动并运行任务
如果对Close()
的调用发生在线程完成自身初始化并完成form
对象实例化之前,那么对Close()
的调用将发现form=Nothing
并且它将简单地返回而不做任何操作
然后更改Show()
代码如下:
Public Overloads Shared Sub Show(ByVal caption As String, ByVal title As String)
_caption = caption
_title = title
If form Is Nothing Then
_thread = New Thread(AddressOf CreateInstance)
_thread.SetApartmentState(ApartmentState.STA) 'should do this '
_thread.IsBackground = True
_thread.Start()
End If
While form Is Nothing ' add '
Thread.Sleep(1) ' this '
End While ' here '
End Sub
将强制Show()
方法阻塞,直到工作线程创建了form
对象-这确保了以后对Close
的所有调用都不会被简单地跳过(因为form is nothing
)。不漂亮,但如果不重构整个类,这可能是最快的修复方法
然而,这实际上是处理长操作的一种非常糟糕的方法。你不是将工作放入工作线程,而是创建一个新的UI线程来运行一个愚蠢的动画,同时阻止真正的UI线程来执行一个耗时的操作。除了糟糕的设计实践之外,这还影响到你的DevExpress widget表单将悬停在所有其他应用程序的顶部(因为从此类调用时,它本身就是一种应用程序),拒绝您的用户与其他应用程序进行多任务处理的能力,因为您的应用程序正在窃取前端和中心,以显示它正在处理某些事情
您确实应该使用ThreadPool、BackgroundWorker或其他类型的工作线程来执行复杂的操作。这样,您的主UI线程就可以自由地提供长操作的状态更新,并避免上述双UI线程混乱。如果您将其称为:
Private Sub doSomethingLong()
HerculesWaitForm.Show("hi", "there")
Sleep(someAmount) 'Random long operation '
HerculesWaitForm.Close()
End Sub
然后,正如您所注意到的,如果打开和关闭调用之间的时间很短,您可能会遇到问题。这是因为对Show()的调用
在执行操作时,启动第二个线程创建新的hercules对象。长操作立即开始,同时线程开始启动并运行任务
如果对Close()
的调用发生在线程完成自身初始化并完成form
对象实例化之前,那么对Close()
的调用将发现form=Nothing
并且它将简单地返回而不做任何操作
然后更改Show()
代码如下:
Public Overloads Shared Sub Show(ByVal caption As String, ByVal title As String)
_caption = caption
_title = title
If form Is Nothing Then
_thread = New Thread(AddressOf CreateInstance)
_thread.SetApartmentState(ApartmentState.STA) 'should do this '
_thread.IsBackground = True
_thread.Start()
End If
While form Is Nothing ' add '
Thread.Sleep(1) ' this '
End While ' here '
End Sub
将强制Show()
方法阻塞,直到工作线程创建了form
对象-这确保了以后对Close
的所有调用都不会被简单地跳过(因为form is nothing
)。不漂亮,但如果不重构整个类,这可能是最快的修复方法
然而,这实际上是处理长操作的一种非常糟糕的方法。你不是将工作放入工作线程,而是创建一个新的UI线程来运行一个愚蠢的动画,同时阻止真正的UI线程来执行一个耗时的操作。除了糟糕的设计实践之外,这还影响到你的DevExpress widget表单将悬停在所有其他应用程序的顶部(因为从此类调用时,它本身就是一种应用程序),拒绝您的用户与其他应用程序进行多任务处理的能力,因为您的应用程序正在窃取前端和中心,以显示它正在处理某些事情
您真的应该使用线程池、BackgroundWorker或其他类型的工作线程来执行复杂的操作。这样,您的主UI线程就可以自由地提供长操作的状态更新,并避免上述双UI线程混乱。您需要调用_-thread.SetApartmentState(ApartmentState.STA)在启动线程之前。您需要调用_thread.SetApartmentState(ApartmentState.STA)在启动线程之前。您在这方面有任何异常吗?无论如何,请检查这个问题:如果操作完成得很快,那么问题仍然存在,但是如果需要一些时间,那么它会按预期工作。一堆代码实际上没有Form.Close()调用。HerculesWaitForm是一个类型名,永远不要使用HerculesWaitForm.Close()。当你在线程中这样做时,它使用了错误的对象。@HansPassant-我也这么认为,但是
HerculesWaitForm.Close()
实际上调用了共享的、重载的Close()
函数来正确关闭表单(只要那时表单已经创建)整个班级都是一个残暴的混乱的方法来实现一个单例,而且它似乎真的“有效”",至少达到了预期的效果。无论如何,我称整个过程为犯规。它会尽可能多地勾选错误实践框…你在这方面有什么例外吗?无论如何,请检查这个问题:如果操作完成得非常快,那么问题仍然存在,但如果需要一些时间,那么它会按预期工作。一堆代码,实际上没有Form.Close()调用。HerculesWaitForm是一个类型名,千万不要使用HerculesWaitForm.Close()。在线程中这样做时,它使用了错误的对象。@HansPassant-我也这么认为,但是HerculesWaitForm.Close()<