0
votes

I am pretty new to C# and having a little issue with something. I believe threading may be the answer, but thats just a Buzz-Word I have picked up when looking for solutions.

namespace Test
{
    public partial class Form1 : Form
    {
        private Form2 form2;

        public Form1()
        {
            InitializeComponent();
            form2 = new Form2();
        }

        private void runCheck(object source, System.Timers.ElapsedEventArgs e)
        {
            form2.ShowDialog();
            form2.TopMost = true;
        }

        private void runCheckFalse()
        {
            form2.Hide();
        }
}

This is only a quick code snippet of my stripped out application, however when trying this I get an error: Cross-thread operation not valid: Control 'Form2' accessed from a thread other than the thread it was created on.

Also as a side note I am using form2.TopMost = true; to attempt to open the window on top of everything else, but this often ends up at the back behind Visual Studio etc

3
Do you have any threading code in your project?Mark Byers
On which line does the error occur?Bob Horn
Are you using MDI form ?HatSoft
Try using the System.Windows.Forms.Timer instead as it's probably here that your exception is being thrown (The WindowsForms timer elapsed event will happen on the GUI thread, System.Timers on a background thread). Otherwise, read up on InvokeRequired and BeginInvoke.dash
@BobHorn Error is thrown on the runCheckFalse under form2.Hide();Matt Clements

3 Answers

4
votes

You need to use Invoke in order to work with the form from a different thread.

Here is a nice article explaining how to work with Windows Forms controls from multiple threads: How to: Make Thread-Safe Calls to Windows Forms Controls

Try this:

namespace Test
{
    public partial class Form1 : Form
    {
        private Form2 form2;

        public Form1()
        {
            InitializeComponent();
            form2 = new Form2();
        }

        private void runCheck(object source, System.Timers.ElapsedEventArgs e)
        {
            if (form2.InvokeRequired)
            {
                form2.Invoke(new EventHandler(delegate { form2.ShowDialog(); form2.TopMost = true; }));
            }
            else
            {
                form2.ShowDialog(); 
                form2.TopMost = true;
            }
        }

        private void runCheckFalse()
        {
            if(form2.InvokeRequired)
            {
                form2.Invoke(new EventHandler(delegate { form2.Hide(); }));
            }
            else
            {
                form2.Hide();
            }
        }
    }
}
1
votes

You can modify your runCheckFalse method in the following way - this is a fairly standard pattern for Windows Forms:

private void runCheckFalse()      
{       
    if(InvokeRequired)
      {
            BeginInvoke(new MethodInvoker(runCheckFalse));
            return;
      }                    
    form2.Hide();  
}

Effectively, what this does is check to see if we are running on the GUI thread (" if InvokeRequired"). If we aren't, we call ourselves on the GUI thread and immediately return. If we are running on the GUI thread, then we don't need to do anything and just continue with the method as normal.

If you need to use parameters:

private void runCheckFalse(bool someParameter)      
{       
    if(InvokeRequired)
      {
            BeginInvoke(new MethodInvoker(() => { runCheckFalse(someParameter);}));
            return;
      }                    
    form2.Hide();  
}
0
votes

WinForm controls can only be updated from the UI thread. Take a look at this blog post, it gives a number of approaches to making sure that the update occurs on the UI thread. It is a long post, but worth the read. The quick and dirty approach is the first one if you don't have time to read it.

Erick