Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
327 views
in Technique[技术] by (71.8m points)

c# - 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...