开发者

Windows Forms Form hanging after calling show from another thread

开发者 https://www.devze.com 2023-04-12 19:03 出处:网络
I have application which has some networking code which runs asynchronously. I have attached some events to be thrown when no connection to server and I\'m creating some \"operation failed\" form when

I have application which has some networking code which runs asynchronously. I have attached some events to be thrown when no connection to server and I'm creating some "operation failed" form when this happens. The problem is that my form hangs after creation. I read about that and I tried to do with:

public void ShowView()
{
    if (this.InvokeRequired)
    {
        Action a = new Action(ShowView);
        this.Invoke(a);
    }
    else this.Show();
}

And problem was still present. Than I found that if control was not been created that InvokeRequired returns false. So I at my initialization code added:

this.show();
this.hide();

This way it seems to be working. But it is pretty ugly and when my app starts I can see for a moment my form being shown and than disappears. How should I make my form to create all of it controls without showing it, or is there some better solution to this problem?

EDIT: More information. I'm using MVP design pattern. I have Presenter which have dependency to IView. My form implements IView. IView has this ShowView() and HideVIew() methods which I call from my presenter. My presenter receives event from another thread. So Where should I do this thread jumping or how should I solve this?

EDIT2: Here sample app illustrating problem:

public partial class Form1 : Form
    {
        Form2 form;

        public Form1()
        {
            Init开发者_Go百科ializeComponent();
            form = new Form2();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //form.Show();
            //form.Hide();
            Thread t = new Thread(new ThreadStart(ShowForm2));
            t.Start();
        }

        private void ShowForm2()
        {
            if (form.InvokeRequired)
            {
                Action a = new Action(ShowForm2);
                form.Invoke(a);
            }
            else
            {
                form.Show();
                Thread.Sleep(5000);
            }
        }
    }

Can you tell me on this concrete problem what to change?


As first step remove recursion from ShowForm2() by using:

Action a = new Action(() => form.Show());

Now detailed explanation what happens: when these lines are commented in button1_Click()

    //form.Show();
    //form.Hide();

then in ShowForm2() form.InvokeRequired will be false. That means that form is executing in same thread as your work and that is why form "hangs".

But when you uncomment these lines, then same form.InvokeRequired will be true, meaning that form is executing in UI thread, and that is why form2 is responsive.

Solution is to force form2 to run in UI thread, but you don't want flickering as in your example, so you must try that with some other method.

Solution is to use form.Handle property after creating form. Form.Handle is created when first used. In your case that was on form.Show(). Obviously it is important to create Handle in desired thread, not only form wrapper. I will attach modified code to make things more clear.

I am not sure that explanation is correct, but handle = form.Handle; will fix your problem.

public partial class Form1 : Form
{
    Form form;
    IntPtr handle;

    public Form1()
    {
        InitializeComponent();
        form = new Form();
        handle = form.Handle;
    }

    private void ShowForm2()
    {
        if (form.InvokeRequired)
        {
            Action a = new Action(() => form.Show());
            form.Invoke(a);
        }
        else
        {
            form.Show();
            Thread.Sleep(5000);
        }
    }

    private void button1_Click_1(object sender, EventArgs e)
    {
        //form.Show();
        //form.Hide();
        Thread t = new Thread(new ThreadStart(ShowForm2));
        t.Start();
    }
    }


The window handle will be created during the Show-call. So it's always good to show forms in the main UI thread! Just switch to that thread and then call Show().


Your understanding of Invoke and InvokeRequired is a little off; InvokeRequired will return true anytime a control is being accessed from a thread other than the thread it was created on (usually called the "UI Thread").

So if you're attempting to call Show() or Hide() from another thread, you do indeed need to Invoke it.

Other than that brief explanation, you haven't provided enough information to really offer any other ideas. Maybe you can post some relevant code, like any code that executes when the form is loaded or activated.

EDIT

You need to get back to your UI thread before you create and show the new form. As has been pointed out in comments, showing it as your application starts up and then hiding it works because that is all happening on the UI thread.

One way you can do this is, if you have a "MainForm" that is always visible, you can move your ShowView method to that form, and use the InvokeRequired`Invoke` pattern to keep the work on the UI Thread.

Another option is to set the WindowState to Minimized by default, so that when it is initially shown (at application start) it's not visible on the screen (you could also set the ShowInTaskbar to false). Then your ShowView method could also change the WindowState.

0

精彩评论

暂无评论...
验证码 换一张
取 消