Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to construct form on background thread, then display on UI thread

UPDATE: Just to summarize what my question has boiled down to:

I was hoping that constructing .NET forms and controls did NOT create any window handles -- hoping that process was delayed until Form.Show/Form.ShowDialog

Can anyone confirm or deny whether that is true?


I've got a large WinForms form with tab control, many many controls on the form, that pauses while loading for a couple seconds. I've narrowed it down to the designer generated code in InitializeComponent, rather than any of my logic in the constructor or OnLoad.

I'm well aware that I can't be trying to interact with the UI on any thread other than the main UI thread, but what I'd like to do is to have the application pre-load this form (run the constructor) in the background, so it's ready for display on the UI thread instantly as soon as the user wants to open it. However, when constructing in a background thread, on this line in the designer:

this.cmbComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;

I'm getting the error

Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.

Now this is halfway down the designer file, which gives me hope that in general this strategy will work. But this particular line seems to be trying to instantly kick off some kind of OLE call.

Any ideas?

EDIT:

I think I'm not making myself clear here. The delay seems to take place during the construction of a bazillion controls during the designer-generated code.

My hope was that all this initialization code took place without actually trying to touch any real Win32 window objects since the form hasn't actually been shown yet.

The fact that I can set (for example) Label texts and positions from this background thread gave me hope that this was true. However it may not be true for all properties.

like image 308
Clyde Avatar asked Jan 15 '10 16:01

Clyde


People also ask

Which method is used to go back to UI thread from a background thread?

You can use runOnUiThread() method of activity to update UI from background thread. Or you can use a android Handler to achieve this.

How to run code in background thread android?

Using handlers To specify the thread on which to run the action, construct the Handler using a Looper for the thread. A Looper is an object that runs the message loop for an associated thread. Once you've created a Handler , you can then use the post(Runnable) method to run a block of code in the corresponding thread.

What is the UI thread C#?

A UI thread creates UI elements and waits and responds to events like mouse clicks and key presses. You can only access the UI elements from the UI thread. There are two types of threads: background and foreground. A UI thread is an example of a foreground thread.

Why we use invoke in C#?

Calling Invoke helps us because it allows a background thread to "do stuff" on a UI thread - it works because it doesn't directly call the method, rather it sends a Windows message that says "run this when you get the chance to".


1 Answers

While it is not possible to create a form on one thread, and display it using another thread, it is certainly possible to create a form in a non main GUI thread. The current accepted answer seems to say this is not possible.

Windows Forms enforces the Single Threaded Apartment model. In summary this means that there can only be one Window message loop per thread and vice versa. Also, if for example threadA wants to interact with the message loop of threadB, it must marshal the call through mechanisms such as BeginInvoke.

However, if you create a new thread and provide it with it's own message loop, that thread will happily process events independently until it is told to end the message loop.

So to demonstrate, below is Windows Forms code for creating and displaying a form on a non GUI thread:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();

    }

    private void button1_Click(object sender, EventArgs e)
    {
        ThreadStart ts = new ThreadStart(OpenForm);

        Thread t = new Thread(ts);
        t.IsBackground=false;

        t.Start(); 
    }

    private void OpenForm()
    {
        Form2 f2 = new Form2();

        f2.ShowDialog();
    }
}


public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
    }

    private void Form2_Load(object sender, EventArgs e)
    {
        label1.Text = Thread.CurrentThread.ManagedThreadId.ToString() ;

    }
}

The OpenForm method runs in a new thread and creates an instance of Form2.

Form2 is actually given it's own separate message loop by calling ShowDialog(). If you were to call Show() instead, no message loop would be provided and Form2 would close immediately.

Also, if you try accessing Form1 within OpenForm() (such as using 'this') you will receive a runtime error as you are trying to do cross-thread UI access.

The t.IsBackground=false sets the thread as a foreground thread. We need a foreground thread because background threads are killed immediately when the main form is closed without first calling FormClosing or FormClosed events.

Apart from these points, Form2 can now be used just like any other form. You'll notice that Form1 is still happily running as usual with it's own message lopp. This means you can click on the button and create multiple instances of Form2, each with their own separate message loop and thread.

You do need to be careful about cross Form access which is now actually cross-thread. You also need to ensure that you handle closing of the main form to ensure any non main thread forms are closed correctly.

like image 95
Ash Avatar answered Sep 21 '22 05:09

Ash