4

I'm just trying to run a new thread each time a button click even occurs which should create a new form. I tried this in the button click event in the MainForm:

private void button1_Click(object sender, EventArgs e)
{
    worker1 = new Thread(new ThreadStart(thread1));
    worker2 = new Thread(new ThreadStart(thread2));

    worker1.Start();
    worker2.Start();
}

private void thread1()
{
    SubForm s = new SubForm();
    s.Show();
}

private void thread2()
{
    SubForm s = new SubForm();
    s.Show();
}

The code in the Subform button click event goes like this:

private void button1_Click(object sender, EventArgs e)
{
    int max;
    try
    {
        max = Convert.ToInt32(textBox1.Text);
    }
    catch
    {
        MessageBox.Show("Enter numbers", "ERROR");
        return;
    }

    progressBar1.Maximum = max;

    for ( long i = 0; i < max; i++)
    {
        progressBar1.Value = Convert.ToInt32(i);
    }          
}

Is this the right way? Because I'm trying to open two independent forms, operations in one thread should not affect the other thread.

Or is BackGroundworker the solution to implement this? If yes, can anyone please help me with that?

H H
  • 263,252
  • 30
  • 330
  • 514
SLp
  • 135
  • 3
  • 3
  • 11
  • 1
    I thought Windows applications only have a single GUI event loop per process? Thus all UI stuff need to be done in the main thread? (CMIIW) – adib Aug 14 '10 at 10:31
  • @user: what problem are you specifically trying to solve? – devoured elysium Aug 14 '10 at 10:34
  • If i click on the Button in the mainForm twice, it ofcourse opens two new Forms ie., the Subforms with he progress bar and all. If one input is given to the first Subform, and it runs for a long time (say i have a progress bar which doesnt end ) I cant do any operation on the second SubForm opened. So i thought of opening subforms individually independent of eachother.. – SLp Aug 14 '10 at 10:38
  • 1
    There's no "main thread", and any thread can run its own message loop. – wj32 Aug 14 '10 at 10:44
  • 1
    possible duplicate of [Why Does my Thread Terminate Immediately After Showing a Windows Form?](http://stackoverflow.com/questions/2469601/why-does-my-thread-terminate-immediately-after-showing-a-windows-form) Also: http://stackoverflow.com/questions/2428530/form-show-is-not-showing-its-child-controls and http://stackoverflow.com/questions/3012795/backgroundworker-and-progressbar-show – Hans Passant Aug 14 '10 at 13:13
  • @wj32 for a given HWND, which thread's message loop does mouse clicks in the window are delivered to? – adib Aug 14 '10 at 14:15
  • Each window has an owner thread, and that's where the messages for a window are delivered. As soon as that thread is terminated, the window is destroyed. – wj32 Aug 15 '10 at 03:46

4 Answers4

13

You do not need to run forms in separate threads. You can just call s.Show() on multiple forms normally. They will not block each other.

Of course, if you’re doing something else, like some sort of calculation or other task that takes a long while, then you should run that in a separate thread, but not the form.

Here is a bit of code that will let you create a progress bar that shows progress for a long process. Notice that every time to access the form from inside the thread, you have to use .Invoke(), which actually schedules that invocation to run on the GUI thread when it’s ready.

public void StartLongProcess()
{
    // Create and show the form with the progress bar
    var progressForm = new Subform();
    progressForm.Show();
    bool interrupt = false;

    // Run the calculation in a separate thread
    var thread = new Thread(() =>
    {
        // Do some calculation, presumably in some sort of loop...
        while ( ... )
        {
            // Every time you want to update the progress bar:
            progressForm.Invoke(new Action(
                () => { progressForm.ProgressBar.Value = ...; }));

            // If you’re ready to cancel the calculation:
            if (interrupt)
                break;
        }

        // The calculation is finished — close the progress form
        progressForm.Invoke(new Action(() => { progressForm.Close(); }));
    });
    thread.Start();

    // Allow the user to cancel the calculation with a Cancel button
    progressForm.CancelButton.Click += (s, e) => { interrupt = true; };
}
Timwi
  • 65,159
  • 33
  • 165
  • 230
  • @ Timwi: Thanks for your answer. But I tried it already. My requirement is different actually. Say I open two subforms and a long process is running in the SubForm1, can I access the SubForm2 now ?? – SLp Aug 14 '10 at 10:40
  • @ Timwi: Sorry I didnt see your update the first time. So this is what im trying to do exactly 'run that in a separate thread, but not the form' as u said, but im wondering how? sorry, im new to these and i have just started learning. – SLp Aug 14 '10 at 10:43
  • 1
    @user403489, the advisable thing to do is not to run the long process in SubForm1, but rather run that long process in a BackgroundWorker (or Thread, but BackgroundWorker makes GUI thread interaction easier to manage.) Otherwise you're making SubForm1 non-responsive, which leads to ugly drawing artifacts (no repainting) among other things. – Dan Bryant Aug 14 '10 at 10:49
3

Although I'm not 100% aware of anything that says running completely seperate forms doing completely isolated operations in their own threads is dangerous in any way, running all UI operations on a single thread is generally regarded as good practice.

You can support this simply by having your Subform class use BackgroundWorker. When the form is shown, kick off the BackgroundWorker so that it processes whatever you need it to.

Then you can simply create new instances of your Subform on your GUI thread and show them. The form will show and start its operation on another thread.

This way the UI will be running on the GUI thread, but the operations the forms are running will be running on ThreadPool threads.

Update

Here's an example of what your background worker handlers might look like - note that (as usual) this is just off the top of my head, but I think you can get your head around the basic principles.

Add a BackgroundWorker to your form named worker. Hook it up to the following event handlers:

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Executed on GUI thread.
    if (e.Error != null)
    {
        // Background thread errored - report it in a messagebox.
        MessageBox.Show(e.Error.ToString());
        return;
    }

    // Worker succeeded.
}

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // Executed on GUI thread.
    progressBar1.Value = e.ProgressPercentage;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    // Executed on ThreadPool thread.
    int max = (int)e.Argument;

    for (long i = 0; i < max; i++)
    {
        worker.ReportProgress(Convert.ToInt32(i));
    }
}

Your click handler would look something like:

void button1_Click(object sender, EventArgs e)
{
    int max;

    try
    {
        // This is what you have in your click handler,
        // Int32.TryParse is a much better alternative.
        max = Convert.ToInt32(textBox1.Text);
    }
    catch
    {
        MessageBox.Show("Enter numbers", "ERROR");
        return;
    }

    progressBar1.Maximum = max;

    worker.RunWorkerAsync(max);
}

I hope that helps.

Alex Humphrey
  • 6,099
  • 4
  • 26
  • 41
  • Thanks Mr Alex Humphrey. I do understand by ur answer that a BackgroundWorker is the best practise, but can u please help me with that in this example. Cos, i know what it does, but all the examples I have seen are too long to follow. So just to understand the simple stuffs, I took a small example. I hope this is useful for others too. Thanks. – SLp Aug 14 '10 at 10:54
  • @user403489: I've updated with some sample code - I hope it gives you an idea of how to use BackgroundWorker. – Alex Humphrey Aug 14 '10 at 11:13
  • Thanks Mr Alex. Your help is of great use for people like me. – SLp Aug 14 '10 at 11:16
  • @ Alex: I tried this and it works. Thank you so much. I used ' Application.Run(s); ' instead of s.show that i used originally and 'Int32.TryParse' doesnt seem to work, 'No overload for method 'TryParse' takes '1' arguments ' is the error i get. I searched for the solution. I declared 'max' as 'int' and i used 'Int32.TryParse' only. But i still get that error. neway, my Main doubt regarding the GUI form is solved. Thank you all. – SLp Aug 14 '10 at 12:03
  • @SLp: The reason I said use Int32.TryParse is so you can avoid swallowing exceptions. Use something like int max = 0; if(Int32.TryParse(textBox1.Text, out max)){ // Text is an integer. } else { // Text is not a valid integer. } - and could you click the tick mark next to your chosen answer so people with the same problem reading your question can identify the best approach? Cheers. – Alex Humphrey Aug 14 '10 at 12:57
  • @AlexHumphrey, I am facing similar issue. I want to run a form in a different thread. I do not want to perform anything in that form. The new form will just act as a message popup. I have oped a new question please take a look at:[link](http://stackoverflow.com/questions/24955750/open-a-form-in-new-thread) How can I achieve this? – Arti Jul 26 '14 at 04:25
2

Try this. It runs the new Form on its own thread with its own message queues and what not. Run this code:

new Thread(new ThreadStart(delegate
{
   Application.Run(new Form());
})).Start();

Use Thread.CurrentThread.GetHashCode() to test that is runs on different thread.

Willie Visagie
  • 171
  • 1
  • 14
0

It's possible to run different forms on different threads. There are two caveats I'm aware of:

  1. Neither form may be an MDI client of the other. Attempting to make a form an MDI client of another when the forms have different threads will fail.
  2. If an object will be sending events to multiple forms and all forms use the same thread, it's possible to synchronize the events to the main thread before raising it. Otherwise, the event must be raised asynchronously and each form must perform its own synchronization mechanism for incoming events.

Obviously it's desirable not to have any window's UI thread get blocked, but using separate threads for separate windows may be a nice alternative.

supercat
  • 77,689
  • 9
  • 166
  • 211