1

I am trying to open a Form from a background thread (I think) because when I call formName.Show(); is freezes that form (Not the main form).

Goal:

When the user receives a new message, popup a newMessageFrm with the new message for reply.

Problem:

The new Form locks.

Here is the code I am using:

static void OnNewMessage(object sender, S22.Xmpp.Im.MessageEventArgs e)
        {
            if(CheckIfFormIsOpen(e.Jid.ToString(), e.Message.ToString()) == true){

            } else
            {
                newMessageFrm tempMsg = new newMessageFrm(e.Jid.ToString());

                tempMsg._msgText = e.Jid.ToString() + ": " + e.Message.ToString();
                tempMsg.frmId = e.Jid.ToString();
                tempMsg.Show(); //This locks up the application
            }


        }

I am using Visual Studio 2015, C#, and S22.Xmpp (As you can see from the code.)

When this event fires off the form does popup, but then locks.

Please let me know if you need any more information.

huysentruitw
  • 27,376
  • 9
  • 90
  • 133

2 Answers2

3

Unfortunately I don't know how to do this without any existing form. But I'm sure you have some kind of main form you can access. Or you can obtain a Form using

var invokingForm = Application.OpenForms[0];

So you can change the code of your method like this:

static void OnNewMessage(object sender, S22.Xmpp.Im.MessageEventArgs e)
{
    var invokingForm = Application.OpenForms[0]; // or whatever Form you can access
    if (invokingForm.InvokeRequired)
    {
        invokingForm.BeginInvoke(new EventHandler<S22.Xmpp.Im.MessageEventArgs>(OnNewMessage), sender, e);
        return; // important!!!
    }

    // the rest of your code goes here, now running
    // on the same ui thread as invokingForm
    if(CheckIfFormIsOpen(e.Jid.ToString(), e.Message.ToString()) == true)
    {
    }
    else
    {
        newMessageFrm tempMsg = new newMessageFrm(e.Jid.ToString());
        tempMsg._msgText = e.Jid.ToString() + ": " + e.Message.ToString();
        tempMsg.frmId = e.Jid.ToString();
        tempMsg.Show();
    }
}

Note that I assumend that S22.Xmpp.Im.MessageEventArgs is inherited from System.EventArgs.

René Vogt
  • 43,056
  • 14
  • 77
  • 99
3

In WinForms there is still a requirement that the UI objects need to be on a thread with a message pump. This is usually the main application thread, also called the UI thread.

In Winforms checking to see if you are on the right thread to access a UI object is done with Control.InvokeRequired. If this returns true, it means you are not on the proper thread, and need to use an Invoke operation.

Since you know you are one a different thread, you don't need to check, you can just Invoke.

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

    private void button1_Click(object sender, EventArgs e)
    {
        ThreadPool.QueueUserWorkItem(BadBackgroundLaunch);
        ThreadPool.QueueUserWorkItem(GoodBackgroundLaunch);
    }

    private void GoodBackgroundLaunch(object state)
    {
        this.Invoke((Action) (() =>
        {
            var form2 = new SecondForm();
            form2.Text = "Good One";
            form2.Show();
        }));
    }

    private void BadBackgroundLaunch(object state)
    {
        var form2 = new SecondForm();
        form2.Text = "Bad one";
        form2.Show();
    }
}
bigtlb
  • 1,512
  • 10
  • 16
  • How do you know OP's method is declared inside a `Form` class? He only showed that it's a _static_ method. So you left out on which `Control` he should call `Invoke`. – René Vogt Jan 06 '16 at 21:42
  • Any `Control` in the application will do. They should all be on the UI thread. Your approach `Application.OpenForms[0]` is fine. – bigtlb Jan 06 '16 at 21:46
  • Yes, that is why I supposed that finding a control is part of the question. – René Vogt Jan 06 '16 at 21:49
  • The question doesn't specify that the OP can't find an existing control. – bigtlb Jan 06 '16 at 21:51