从另一个线程打开时,Windows窗体不关闭

时间:2013-03-13 09:26:59

标签: .net vb.net winforms devexpress .net-2.0

我创建了一个简单的窗口表单(粘贴在下面)。现在我使用它在运行耗时的复杂操作时向用户显示等待dailog。

调用HerculesWaitForm.Show("Some Title","Some Caption...)会像往常一样显示表单,但在调用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

2 个答案:

答案 0 :(得分:2)

如果您将其称为:

Private Sub doSomethingLong()
    HerculesWaitForm.Show("hi", "there")
    Sleep(someAmount)  'Random long operation '
    HerculesWaitForm.Close()
End Sub

如果您注意到,打开和关闭通话之间的时间很短,那么您可能遇到问题。这是因为对Show()的调用启动了第二个线程,正在创建新的大力神对象,而正在执行您的操作。长时间操作立即开始,同时线程开始执行自我启动和运行的任务。

如果在线程完成初始化并完成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的所有调用(因为{{1} }})。不漂亮,但没有重构整个课程,它可能是最快的修复。

然而,这实际上是处理长时间操作的一种非常糟糕的方式。您正在创建一个新的UI线程来运行愚蠢的动画,同时阻止真正的UI线程执行耗时的操作,而不是将工作放入工作线程。除了是可怕的设计实践,这也有你的DevExpress插件形式将高于所有其他应用程序之上徘徊(因为它是一种应用程序本身的从这个类调用时)的作用,否认你的用户多任务的能力其他应用程序,因为您的应用程序正在窃取正面和中心,以显示它正在处理某些事情。

你真的应该使用ThreadPool或BackgroundWorker或其他类型的工作线程来代替复杂的操作。这使得您的主UI线程可以自由地提供长操作的状态更新,并避免上面的双UI线程混乱。

答案 1 :(得分:2)

您需要在启动线程之前调用_thread.SetApartmentState(ApartmentState.STA)。