3

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()
        {
            InitializeComponent();
            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?

Vajda
  • 1,795
  • 6
  • 39
  • 49
  • You need an instance of a Form that has already been created. Shouldn't be hard, use Application.OpenForms[0] if you really have to. – Hans Passant Oct 11 '11 at 14:50
  • ShowView is method from my form which inherits Form class, so I'm calling show method to show my form. Should I call another method which will do form showing? – Vajda Oct 11 '11 at 15:07

3 Answers3

4

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();
    }
    }
watbywbarif
  • 6,487
  • 8
  • 50
  • 64
  • Thanks for this answer, it saved me! `handle = form.Handle;` is quite more elegant than `form.WindowsState = FormWindowState.Minimized; form.ShowInTaskbar = false; form.Show(); form.Hide(); form.WindowsState = FormWindowState.Normal; form.ShowInTaskbar = true;` – Otiel May 20 '13 at 14:39
2

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().

Fischermaen
  • 12,238
  • 2
  • 39
  • 56
  • @Hooch - I don't think Fischermaen understands what he is talking about. – Security Hound Oct 11 '11 at 15:04
  • 2
    Lol, i wrote new answer and played half an hour with this example just to learn that Fischermaen gave us good point about problem, so vote up, and it seams that Ramhound does not understand what is he talking about ;) I ignored this answer because of no votes and bad comments ;( – watbywbarif Feb 10 '12 at 14:15
1

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.

CodingGorilla
  • 19,612
  • 4
  • 45
  • 65
  • Yep, It will return true anytime a control is being accessed from a thread other that the thread it was created on. And if control but if it wasn't created that will return false. Why is that my app works normal if i call first show() hide() from UI thread, and when I don't do that it doesn't work? – Vajda Oct 11 '11 at 15:05
  • @Vajda - After the first time the form is open but hidden. So calls to display the form simply unhide the form. – Security Hound Oct 11 '11 at 15:11
  • Yep, I understand that. But how to avoid this show() hide() calls? – Vajda Oct 11 '11 at 15:14
  • @Vajda Why would you want to? – CodingGorilla Oct 11 '11 at 15:30
  • @Coding Gorilla Because it looks like some workaround, not real solution. Please check my edited post for sample of problem. – Vajda Oct 11 '11 at 15:36
  • @vajda The only problem I see with your sample is the call to `Thread.Sleep`; this is going to put the UI Thread to sleep leaving your application [seemingly] un-responsive. Take that out and everything should work just fine. Also, this is not a "work around" this is a common and accepted practice in Winforms. – CodingGorilla Oct 11 '11 at 15:42
  • No, I don't have Thread.Sleep(). This I put there just in this sample because without that thread is finished right away and my form is disposed, so it is only to provide scenario which I have, but at my situation it stays that way forever, not for only 5 seconds. But it seems that you are right, since my form is not aware of main form, only way to make this work is with show hide and with that minimize and ShowInTaskbar thing. – Vajda Oct 11 '11 at 20:00
  • @Vajda Well if you have some other code that is executing in the same place as that `Thread.Sleep()` call, then that is your problem. That else block needs to be able to finish executing because it's running on the UI thread, so if it blocks then your UI thread is not able to process normally anymore. – CodingGorilla Oct 11 '11 at 20:07