0

I have a GUI with a Named Pipe Server running in a Backgroundworker, which gets commands from the Named Pipe in the DoWork() event handler and interacts with the GUI's controls in the RunWorkerCompleted() event handler.

The GUI sometimes opens Modal windows, that prevent the RunWorkerCompleted() event handler from running until the window is closed.

Is there any way for me to close these Modal windows with the Named Pipe Server and Backgroundworker?

The best thing I've found so far is the hack in this post (How to continue executing code after calling ShowDialog()), where I would use Show() and Parent.Enabled = False instead of ShowDialong(). I'm hoping there is something cleaner, however.

Community
  • 1
  • 1
BigBobby
  • 423
  • 1
  • 3
  • 17
  • A modal window does **not** prevent the RWC event handler from running, it has no such powers. You'll need to look for your problem in another corner. Use the Debug + Windows + Threads debugger window to find out what the UI thread is doing. – Hans Passant Mar 07 '15 at 02:04
  • Thank you for pointing out that I was misunderstanding my problem! You are correct that modal windows weren't preventing RWC from running. The problem actually occurred only when a modal window was *opened* as a result of RWC. For example, if my BGW receives a command in DW to push a button in the GUI, it then enters RWC and calls the button's Click() event handler. If Click() opens a modal window to Confirm/Cancel the button press then RWC does not exit until the Modal window is closed. I do not restart the BGW until the last line of RWC, however, which is why it looks like it locked up. – BigBobby Mar 09 '15 at 22:22

1 Answers1

1

Put a method in the modal form that can be called on a secondary thread, e.g.

Public Sub CloseFromBackground()
    If Me.InvokeRequired Then
        Me.Invoke(New MethodInvoker(AddressOf CloseFromBackground))
    Else
        Me.Close()
    End If
End Sub

You'd obviously need a reference to that form to call it on, so you would not be able to have assigned it to just a local variable. I guess that you could get a reference from the OpenForms collection instead. If you do that then you can use the Modal property of each form to determine which is displayed modally.

E.g.

Imports System.Threading

Public Class Form1

    Private f2 As Form2

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Me.BackgroundWorker1.RunWorkerAsync()

        Me.f2 = New Form2()
        Me.f2.ShowDialog()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Thread.Sleep(2000)
        Me.f2.CloseFromBackground()
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        MessageBox.Show("Done!")
    End Sub
End Class


Public Class Form2

    Public Sub CloseFromBackground()
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf CloseFromBackground))
        Else
            Me.Close()
        End If
    End Sub

End Class
jmcilhinney
  • 50,448
  • 5
  • 26
  • 46
  • Thank you very much. I put your method in the modal form, and when I call CloseFromBackground() in the DoWork() event handler, I am no longer blocked from entering the RunWorkerCompleted() event handler. I am a little confused though, as I was expecting CloseFromBackground() to actually close the Modal form. What it seems to be doing, however, is just allowing the RunWorkerCompleted() event handler to run where I then close the Modal form by calling its OK_Button_Click() event handler. – BigBobby Mar 06 '15 at 21:20
  • That seems odd. I just put together an example and it worked as expected for me. I've added the code to my answer. There may be more going on in your project than you've told us about. – jmcilhinney Mar 07 '15 at 01:03
  • Although the comment from Hans helped me realize that I didn't understand the root cause of my problem, I didn't want to leave your comment unanswered (seeing as how it answers my original question from before I fully understood my problem). – BigBobby Mar 09 '15 at 23:07
  • The different behavior was because I wasn't making a New object for Form2. Instead of "Me.f2.ShowDialog()" I had "Form2.ShowDialog()", and instead of "Me.f2.CloseFromBackground()" I had "Form2.CloseFromBackground()". I thought that VS2010 created a Form2 object from the Form2 class along with all of the other code it generates from the Designer but it seems I was wrong. If I add "Private Form2 As New Form2" to the Form1 class, then it works like your code. I'm surprised I wasn't getting an exception before, when I was calling CloseFromBackground() on a class instead of an object? – BigBobby Mar 09 '15 at 23:18
  • You weren't getting an exception because you WEREN'T calling it on a class. You WERE calling it on an object, i.e. the default instance of the class. The issue is that default instances are thread-specific so, when you called `Form2.CloseFromBackground` on a secondary thread, you were actually creating a new instance of `Form2` and closing that rather than closing the existing instance. For some more information on default instances, read this: http://jmcilhinney.blogspot.com.au/2009/07/vbnet-default-form-instances.html – jmcilhinney Mar 10 '15 at 00:30
  • Thanks, I certainly believe that my understanding of default instances is not clear. Nearly all of my programming is embedded. I have less experience with the higher-level concepts I encounter in GUIs. – BigBobby Mar 10 '15 at 13:48