Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 256
  • Last Modified:

Form not instantiated on inter-thread call OR how to load a form w/o making visible


Hey, I have an MDI application with a child window that is sort of an "output" window.  I also have buttons that start processes on background threads, and sometimes when the process is done it needs to send text to the output window.  So I made my Write functions thread-safe as follows (pretty much parroting another answer from this great site -- I don't understand this as well as I should).

In my OutputWindow form:

    Public Sub Write(ByVal txt As String)
        AppendLine(txt)
    End Sub

    Delegate Sub SetTextCallback(ByVal [text] As String)

    Private Sub AppendLine(ByVal txt As String)

        ' InvokeRequired required compares the thread ID of the
        ' calling thread to the thread ID of the creating thread.
        ' If these threads are different, it returns true.

        If Me.RichTextBox1.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf RawAppendLine)
            Me.Invoke(d, New Object() {txt})
        Else
            RawAppendLine(txt)
        End If

    End Sub

    Private Sub RawAppendLine(ByVal txt As String)

        RichTextBox1.AppendText(vbCrLf + txt)
        ScrollToEnd()

    End Sub

Now... when I start my application, I *New* the form but I do not set Visible=True (which I think means the form and constituent controls do not actually get loaded) unless the user has set an option that he wants it visible on startup.  If this option is not set and it is not made visible, calls to Write from an alternate thread fail with the following exception:

"Invoke or BeginInvoke cannot be called on a control until the window handle has been created."

Now, BEFORE this error the program uses the Write function at startup from the same thread (for messages like "Program started") and when the user eventually shows the window it appears with all the messages, which seems to mean the RichTextBox was loaded.  So this succeeds:

1) Starts invisible (New but no Visible=True)
2) Write from same thread
3) User calls Visible=True (from menu item) (previous writes are visible, which seems to mean it was instantiated?)
4) Write from other thread

And this fails

1) Starts invisible (New but no Visible=True)
2) Write from same thread  (why didn't this instantiate the control, i.e., "create the window handle"?)
3) Write from other thread   <-- above exception!

Anybody have any good theories which fit these observations??  I'd like to (1) fix this and (2) understand a little better.  

Thanks in advance...






0
riceman0
Asked:
riceman0
  • 2
1 Solution
 
AlexFMCommented:
   Public Sub Write(ByVal txt As String)
        AppendLine(txt)
    End Sub

In this function you can test whether form handle is created. You can subscribe to Control.HandleCreated Event and set some flag in the handler for this. If form is not created yet, accumulate all text in some container for example, StringBuilder or List<String>.
When form is shown first time, show information from container to RichTextBox1. From this moment, Write function can call AppendLine and safely use Invoke and RichTextBox1.

Unfortunately, .NET doesn't allow to create valid visible form - you cannot work normally with form controls and execute Invoke unless form is shown first time.
0
 
riceman0Author Commented:

Oh, man: subscribing to events, creating a flag.  

Seems like a bad oversight that they didn't allow you to start a window invisible, would save a lot of headache.

Question before I try all that... I suspect my window HAS been created, since the previous same-thread Write calls work, yes?  Why then this error?



0
 
AlexFMCommented:
Well, possibly Form allows to set control properties before handle is created. But Invoke cannot work before handle is created. Handle is created only when Form.Show function is called, and this immediately makes form visible.
I think that when form is hidden (after it was shown initially), Invoke can fail as well. At least I know, that when form is hidden and shown again, its handle may change.
So, to make this working, I suggest the following algorithm (pseudo-code):

dim handleExists as boolean = false

HandleCreated:
    handleExists = true;
    Add text from container to RichTextBox
    Clear container

HandleDestroyed:
    handleExists = false;

AppendLine:
    if handle doesn't exist: add line to container
    else: existing code
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now