开发者

Closing form from different thread happening too soon, causing exception

I've got the following situation,

Private Sub MyButton_Click(sender as Object, args as EventArgs) Handles MyButton.Click
    Me.pleaseWaitFrm = New PleaseWaitForm()
    ' Fire up new thread to do some work in (AddressO开发者_JAVA技巧f DoMyWork)
    Me.pleaseWaitFrm.ShowDialog()
End Sub

Private Sub DoMyWork()
   Dim log = Me.DoTheActualWork()

   Me.pleaseWaitFrm.Close()

   Using logFrm as New LogViewer(log)
       logFrm.ShowDialog()
   End Using
End Sub

If the DoTheActualWork() call exits fast enough, the Me.pleaseWaitFrm.Close() call is happening during the Me.pleaseWaitFrm.ShowDialog() call. The result, no surprise, is this exception:

An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll

Additional information: Value Close() cannot be called while doing CreateHandle().

Obviously this is the old "you cannot .Close() a WinForm while it's in the process of loading" problem. But what isn't obvious to me is how best to prevent that from happening in this case? How does one safely and reliably delay the .Close() until it is safe to do so in this situation?


Me.pleaseWaitFrm.Close()

That's an illegal call, you are not allowed to close a form from another thread. Not sure how you got away with it, that should raise an IllegalOperationException when you run with a debugger. Review your code and delete any assignment to the Control.CheckForIllegalCrossThreadCalls property.

This exception you're getting is a clear side-effect of this. You must use Control.Invoke() to get the dialog closed. This automatically solves your problem, the delegate target cannot execute until the dialog is loaded.

Leverage the BackgroundWorker class, it makes this easy. You can close the dialog in a RunWorkerCompleted event handler.


Why not set a "ShouldClose" flag which can be checked when it's safe to close the form - and close it if required?

With regards to a code example, I'd implement it slightly differently but let me know if this breaks any other requirements and we can modify it...

    ''in PleaseWaitForm:
    Public Property ShouldClose as boolean = false

Private Sub frmSplash_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Me.Close()
End Sub

    ''Your posted code
    Private Sub MyButton_Click(sender as Object, args as EventArgs) Handles MyButton.Click
        Me.pleaseWaitFrm = New PleaseWaitForm()
        '' Fire up new thread to do some work in (AddressOf DoMyWork)
        Me.pleaseWaitFrm.ShowDialog()
    End Sub

    Private Sub DoMyWork()
       Dim log = Me.DoTheActualWork()


       Me.pleaseWaitFrm.ShouldClose = True
       If Me.pleaseWaitFrm.Created Then
           Me.pleaseWaitFrm.Created.Close
       End If

       Using logFrm as New LogViewer(log)
           logFrm.ShowDialog()
       End Using
    End Sub

In short, if we Can close the form, we do - otherwise, set a flag and the form will do it when it finishes loading.

I tested and didn't get any issues calling Me.Close inside the frmSplash.Load() but if you do encounter any problems, you can make it cast-iron by having a worked on frmSplash which checks the value rather than use the Load event

Edit: Spotted and fixed a bug

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜