Use
if (InvokeRequired)
{
//invoke
}
else
{
//do
}
Most people see that error and see one thing, "you are not accessing this control from the main UI thread." In reality you can have 100 UI threads if you want (the behavior for this is not defined, but supported). Chances are panelMain is created on a different thread to (this); I can't see the code - but it looks like you are creating it in your worker/thread.
To confirm the behavior try this:
Action addAction = new Action (
new delegate { panelMain.Controls.Add(UCLoadingScreen); } )
if(panelMain.InvokeRequired)
{
panelMain.Invoke(addAction); // Breakpoint here.
}
else
{
addAction();
}
Be prepared for a different error (a child control on a different thread to it's parent, not sure what error you will get, but I am pretty certain you will). This is not a fix.
A factory will fix this however:
public void CreateControl<T>() where T : new()
{
if(InvokeRequired)
{
return (T)Invoke(new Func<T>(CreateControl<T>()));
}
return new T();
}
EDIT: panelMain might not be the thread 'violator', as I said, parenting controls from different threads is results in highly undefined behavior. Make sure all your controls are created in context of your main form's thread.
I suspect InvokedRevoked might be lying to you. A WinForm Control defers creation of the Control's underlying Win32 HWND until a method call actually needs it. InvokeRequired will return false if the HWND has not been created yet.
For a detailed explanation, see: "Mysterious Hang or The Great Deception of InvokeRequired"
If your background thread queries InvokeRequired before the UI thread has caused the Control to lazily create its HWND, InvokeRequired will (incorrectly) tell your background thread that it does not need to use Invoke() to pass control back to the UI thread. When the background thread then accesses the Control, BAM! "InvalidOperationException: Cross-thread operation not valid!"
The UI thread can manually force the Control to create its HWND handle to so Control.InvokeRequired will know the UI thread is the Control's owner:
Control control = new Control();
IntPtr handle = control.Handle; // if you ask for HWND, it will be created
I hope this works for you. I assume your gui stuff is on one thread. Just initialize this singleton and rely on it whenever you feel like calling Control.InvokeRequired property.
Cheers,
-Greg
public sealed class UiThread
{
#region Singleton
// Singleton pattern implementation taken from Jon Skeet's C# and .NET article www.yoda.arachsys.com/csharp/singleton.html
UiThread() { }
public static UiThread Instance { get { return Nested.instance; } }
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested() { }
internal static readonly UiThread instance = new UiThread();
}
#endregion
int _uiThreadId = 0;
public void SetUiThread(Thread thread)
{
if (_uiThreadId != 0)
throw new ApplicationException("UI Thread has already been established!");
if (thread.ManagedThreadId == 0)
throw new ArgumentException("Unexpected thread id value of 0!");
if (thread.IsBackground)
throw new ArgumentException("Supplied thread should not be a background thread!");
if (thread.IsThreadPoolThread)
throw new ArgumentException("Supplied thread should not be a thread pool thread!");
_uiThreadId = thread.ManagedThreadId;
}
/// <summary>
/// It's possible for InvokeRequired to return false when running in background thread.
/// This happens when unmanaged control handle has not yet been created.
/// We second-guess Microsoft's implementation in this case, checking against foreground thread's Id.
/// </summary>
/// <param name="control">Control to check against.</param>
public bool InvokeRequired(Control control)
{
if (control.InvokeRequired)
return true;
IntPtr unmanagedHandle = control.Handle;
bool newResult = control.InvokeRequired;
if (unmanagedHandle == IntPtr.Zero)
{
// Trace.WriteLine() call here forces unmanagedHandle's initialization,
// even with optimizing compiler.
Trace.WriteLine(string.Format("Control handle could not be established! Control was {0}.", control.ToString()));
}
bool retVal = InvokeRequired();
// Break whenever the result of our check does not match theirs.
Debug.Assert(retVal == newResult);
// Return our InvokeRequired result over theirs
// to keep with the tradition of updating controls from foreground only.
return retVal;
}
/// <summary>
/// Prefer to use overload with Control argument if at all possible.
/// It's possible for InvokeRequired to return false when running in background thread.
/// This happens when unmanaged control handle has not yet been created.
/// We second-guess Microsoft's implementation in this case, checking against foreground thread's Id.
/// </summary>
public bool InvokeRequired()
{
if (_uiThreadId == 0)
throw new ApplicationException("UI Thread has not been established!");
return (Thread.CurrentThread.ManagedThreadId != _uiThreadId);
}
}