Visual Basic 2008 - New Form on a different thread

12,233

Solution 1

This isn't very common; generally it's best to limit all UI stuff to a single thread. But if you're convinced that you need each form to run on a separate thread, you must take into account the Windows API event handling model. The [over]-simplified version is that each form must have its own message loop to remove event messages from the queue and process them, so if you want to open a form on a new thread, you need to create that message pump.

The easiest way to do that is using the Application.Run method, and let the .NET Framework handle creating that message pump for you. For example:

Dim frm As Form1 = New Form1()
Application.Run(frm)

From looking at the code shown in your question, I can't discern any possible reason why those forms would need to run on separate threads. You can call the Show method of multiple forms so that they will be displayed on the screen at the same time. They won't block each other as long as you don't use the ShowDialog method, which displays each as a modal dialog. This is the way so many applications display multiple toolbox windows and other kinds of forms on the screen at the same time.

If you need to do some type of processor-intensive calculation, you still don't need to run each on a separate thread. Spin up a background thread (the BackgroundWorker class makes this very simple) and update the appropriate form's UI using the Invoke method.

Solution 2

You can certainly do this on Win32 but I don't know how well this maps over to .net.

The essential issue is that window handles have thread affinity. So you really need all interaction with them to happen in that thread. Essentially this means that you create all the window handles associated with that form in its thread. You also need to run a message loop in the thread.

The reason that people usually run all the UI out of the main thread and handle long-running actions in separate threads is that it is easier that way. You should ask yourself again why you want to do it this non-standard way.

I suspect you are not quite seeing the full picture. The need for threads in a desktop app principally arises when you have long running actions. Usually you want to keep your UI responsive and providing feedback for the long running action. Doing so leads to threads.

However, in your proposed solution you now have a multitude of extra threads and complexity, and you are still faced with the original problem! A long running action on one of your forms will hang it unless you perform that action in a separate thread, and once again we reach the standard solution to the problem.

Share:
12,233
David Workman
Author by

David Workman

Updated on June 04, 2022

Comments

  • David Workman
    David Workman almost 2 years

    My “form1” is just a simple page with buttons that launch different forms which do all the work, the "form1" code for the first four buttons is below.

    What I want is for each form to run in a separate thread.

    Public Class Main
        Private Sub btnDownLoadStockPrices_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDownLoadStockPrices.Click
            LoadStocksFromDownloadSite.Visible = True
        End Sub
    
        Private Sub btnLoadOptionsIntoDatabase_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoadOptionsIntoDatabase.Click
            LoadOptionsIntoDatabase.Visible = True
        End Sub
    
        Private Sub btnVerifyDatabases_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnVerifyDatabases.Click
            VerifyDatabase.Visible = True
        End Sub
    
        Private Sub btnAnalyzeStock_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAnalyzeStock.Click
            AnalyzeSingleStock.visible = True
        End Sub
    End Class
    

    I’ve found plenty of code examples to have different buttons on a single form run a subroutine in a separate thread, but for some reason I can’t seem to apply it to an entire form.

    I think it’s something simple, like I need to tie each new thread as a handle to each forms “load” routine, but I just can’t get it to work. I don’t care about “synchronizing” threads at all, as each form is really a completely different functional program.

    Any help would be much appriciated!

  • David Workman
    David Workman about 13 years
    Thanks for the help ... the reason I want the different threads is that some of the functions wait till a specific time of day to download "end of day" stock and option data from my data provider. While these tasks are waiting/downloading, I'd like to be able to use the other forms. I suppose I can just put these specific sub-routines on a new thread, but I thought it would be easier for the each form to be on it's own.
  • Cody Gray
    Cody Gray about 13 years
    @David: Then the latter part of my answer is what you're looking for. There's absolutely no reason to run the forms on separate threads just so that they'll be available at different times. In fact, since you say that you're not even trying to communicate between forms, your task is even easier. Just create a new instance of a form class, and call frm.Show(). You can have as many of them on the screen at a time as you want, and interact with each separately. If you want to do downloading on a background thread, that's simple enough too. There are lots of examples online of BackgroundWorker.
  • David Workman
    David Workman about 13 years
    Thanks for the quick response. I think what's hanging me up is that all the forms are part of the project and they all get loaded automatically on startup, and I'm just making them "visible" when I click on the button in Form1. I guess I was trying to find a way to have them load only when I click on the button, and do that in a new thread. I will try putting the waiting/downloaded process in BackgroundWorker and see if that makes it so I can use the other forms at the same time.
  • Cody Gray
    Cody Gray about 13 years
    @David: Why do all the forms get loaded automatically on startup? Do you intend for that to happen? You know that you can load and show a new form object any time that you want via code, right? Dim frm As Form1 = New Form1() : frm.Show() Just put something like that into your button's Click event handler method. It's still not clear where you got the idea that you need threads. As David's answer suggests, threads do have their purpose, but more often than not, they just add unnecessary complexity. I think this is a case of the latter.
  • David Workman
    David Workman about 13 years
    Thanks David - as noted in my other reply to Cody, I don't care if that one form "hangs" while it is waiting and downloading, but I want to use all the other forms in the meantime. I have never used any sort of multi-threading at all, so I'm not sure what approach is "standard" and what is "non-standard" :-)
  • David Workman
    David Workman about 13 years
    Actually, I can't figure out how to prevent the other forms from loading on startup ... they were created in Visual Studio by using "Project -> Add Windows Form". I do not have any "New Form" commands anywhere, they are just "there" and I'm setting the form's property to "visible" when I want it to show up.
  • Cody Gray
    Cody Gray about 13 years
    @David: Ah, yes. VB.NET is hiding an implementation detail from you to preserve backwards compatibility with VB 6. There's a default instance of every form class in your project, so that all you have to do is call a method on it. Hans Passant's answer here is excellent in explaining that behavior. In short, there's really no reason to worry about it. The real question is what's wrong with your code as shown? Do you have problems with the UI being unresponsive?
  • David Workman
    David Workman about 13 years
    Ok, thanks - that makes sense, I'll look at that link. The issue I have is that I have two different forms for downloading stock and option data from the internet, one of them at various times during the day and then the "end of day" data at around 5pm. While these forms are waiting, downloading, and stuffin the data into the SQL database, all the other forms are hung.
  • Cody Gray
    Cody Gray about 13 years
    @David: Yeah, then pushing that downloading/updating off into a new BackgroundWorker is the best option. In fact, that will keep even the form being updated from getting hung. Not that you need that, but it definitely looks bad from a usability perspective if Windows whites out the window and it looks like it's crashed because it's been unresponsive. Like I mentioned before, there are lots of examples around the web and here on SO about using BackgroundWorker and the Invoke method to do that. Actually, the sample code on MSDN (with the documentation, check my answer) is pretty good.
  • David Heffernan
    David Heffernan about 13 years
    @David I still think it will be much easier to run all your UI out of the main thread and put the long running calculations in separate threads. If you let a UI thread hang then the system gets a bit snotty about it and fades out your Window and threatens to kill it because it's not responding. Worker threads for the calcs is probably the easier option too as well as better.
  • David Workman
    David Workman about 13 years
    Excellent, I really appriciate the help.
  • David Workman
    David Workman about 13 years
    Thanks, I appriciate the guidance from you and Cody. I was thinking that it would be a simple matter to just load each form in a new thread, and that way I could program whatever I wanted on each form and not worry about managing anything else. I am going to investigate using the BackgroundWorker for the waiting/downloading/database stuffing as Cody suggests above - that looks like the best option.
  • David Workman
    David Workman about 13 years
    @Cody - Just an update ... after a few false starts, I have the BackgroundWorker going just fine on two forms and it works like a charm! My main textbox on both forms is just for "output" status from the downloader and database stuffer, it never gets updated from anywhere else, so instead of going through all the hassle of changing dozens of txtOutput.AppendText lines to use the "invoke" routine, I just set the control to "CheckForIllegalCrossThreadCalls=false". I know this isn't the "proper" way to do it, but it works just fine.