InvokeRequired=true but BeginInvoke fails causing freeze - c#

C# windows forms VS 2013 OS:Win7
I am having an interesting problem where invokeRequired is true but when I call beginInvoke() it never executes and the window never closes.
However when I remove beingInvoke() altogether the window closes ok.
public void CloseMyForm()
{
//if I remove this if block altogether including beingInvoke() the window closes ok
if ( !this.IsDisposed && this.InvokeRequired && this.IsHandleCreated )
{
log("callin begininvoke()"); //this is logged correctly
this.BeginInvoke((MethodInvoker)delegate() { CloseMyForm(); });
return;
}
log("outside of begin invoke"); //this is never logged
this.Close();
}
CloseMyForm is called by a separate thread which is created like this at the startup. Please note this is not the main window but a separate window open from the main form.
Thread connectThread = new Thread(new ThreadStart(CheckWhenToCloseMyForm));
public void CheckWhenToCloseMyForm()
{
while (true)
{
CallSomeFunc();
CallSomeFunc1();
if (allconditionsmet)
{
System.Threading.Thread.Sleep(1000);
CloseMyForm();
break;
}
}
}

The BeginInvoke is made available via the base Control class.
Executes a delegate asynchronously on the thread that the control's underlying handle was created on
If the InvokedRequired property is actually true, that means "the caller must call an invoke method when making method calls to the control because the caller is on a different thread than the one the control was created on".
It looks like you're incorrectly calling BeginInvoke, you should try calling Invoke instead.
Executes the specified delegate on the thread that owns the control's underlying window handle
public void CloseMyForm()
{
if (!this.IsDisposed && this.InvokeRequired && this.IsHandleCreated)
{
log("calling invoke()");
this.Invoke((MethodInvoker)delegate() { CloseMyForm(); });
}
else
{
log("outside of invoke"); // this is never logged
this.Close();
}
}
Check out this neat little extension method that could help simplify this. With this you could write your close method like this instead.
public void CloseMyForm()
{
this.ThreadSafeInvoke(() => this.Close());
}

Okay, now that you have provided this snippet I understand the issue.
Thread connectThread = new Thread(new ThreadStart(CheckWhenToCloseMyForm));
public void CheckWhenToCloseMyForm()
{
while (true)
{
CallSomeFunc();
CallSomeFunc1();
if (allconditionsmet)
{
System.Threading.Thread.Sleep(1000);
CloseMyForm()
}
}
}
In your while loop you need to break or return after you invoke CloseMyForm. That's it...very simple. You can use either BeginInvoke or Invoke.

Related

Cross-thread operation not valid...Invoke and delegate seems useless

I have a issue with thread, I've searched for a few days but still cannot solve it..
Due to some reason, I customize a progress form and use it in threads.
I tried to write all functions inside the progress form so that they are wrapped by Invoke and delegate. Unfortunately, this code is not working properly since this.InvokeRequired is returning false when I expected it to return true.
The problem is, when I execute the program, sometimes it throw an exception:
Cross-thread operation not valid: Control 'FormProgress' accessed from a thread other than the thread it was create on.
Here's the code of progress form.
I've wrapped all functions with Invoke and delegate.
public partial class FormProgress : Form
{
public FormProgress()
{
InitializeComponent();
}
public void SetStatusLabelText(string text)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker) delegate
{
label1.Text = text;
});
}
else
{
// exception thrown here
label1.Text = text;
}
}
public void SetDialogResult(DialogResult dialogResult)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
if (DialogResult == DialogResult.None)
this.DialogResult = dialogResult;
});
}
else
{
if (DialogResult == DialogResult.None)
this.DialogResult = dialogResult;
}
}
}
Here's the code of thread, the exception throws when I click button1
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
for (int i=0; i<100; i++)
ProgressTest();
}
private void ProgressTest()
{
FormProgress dialog = new FormProgress();
{
Thread threadTest = new Thread(delegate ()
{
dialog.SetStatusLabelText("initial....(1)");
Thread.Sleep(50);
dialog.SetStatusLabelText("initial....(2)");
Thread.Sleep(50);
dialog.SetStatusLabelText("initial....(3)");
Thread.Sleep(50);
dialog.SetDialogResult(DialogResult.OK);
});
threadTest.Name = "ThreadTest";
threadTest.Start();
if (dialog.ShowDialog() == DialogResult.Cancel)
{
if (threadTest.IsAlive)
threadTest.Abort();
}
threadTest.Join();
}
}
}
As per the docs:
If the control's handle does not yet exist, InvokeRequired searches up
the control's parent chain until it finds a control or form that does
have a window handle. If no appropriate handle can be found, the
InvokeRequired method returns false.
This means that InvokeRequired can return false if Invoke is not
required (the call occurs on the same thread), or if the control was
created on a different thread but the control's handle has not yet
been created.
In the case where the control's handle has not yet been created, you
should not simply call properties, methods, or events on the control.
This might cause the control's handle to be created on the background
thread, isolating the control on a thread without a message pump and
making the application unstable.
You can protect against this case by also checking the value of
IsHandleCreated when InvokeRequired returns false on a background
thread. If the control handle has not yet been created, you must wait
until it has been created before calling Invoke or BeginInvoke.
Typically, this happens only if a background thread is created in the
constructor of the primary form for the application (as in
Application.Run(new MainForm()), before the form has been shown or
Application.Run has been called.
The issue you have is that some of your InvokeRequired calls may be occurring before the form has been shown. This is because you are starting your new thread before calling dialog.ShowDialog(). Note, as is common with race conditions, the problem won't always occur - just sometimes.
As per above, you may want to consider checking IsHandleCreated before executing the logic in your else blocks (after checking InvokeRequired) to protect against this possibility. Alternatively, rethink your entire strategy around the progress form.
do changes with controls inside if (this.InvokeRequired) block
remove the else block staying after if (this.InvokeRequired)
public void SetStatusLabelText(string text)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker) delegate
{
label1.Text = text;
});
}
}
public void SetDialogResult(DialogResult dialogResult)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
if (DialogResult == DialogResult.None)
this.DialogResult = dialogResult;
});
}
}
let's consider method ProgressTest(), what happaning:
after threadTest.Start() has been called , the threadTest method starts execution of his work item in a new thread
after dialog.ShowDialog() the GUI thread become blocked , it makes this.InvokeRequired = false
at the same time threadTest keep working and when threadTest try to execute
else
{
label1.Text = text;
}
label1.Text setter is called from NONE GUI thread (it is called from "ThreadTest" thread), that's why you get exception
It should be noted that dialog.SetStatusLabelText("initial....") which supposed to be called 300 times , actually will be called less then 300

Making comparision in one thread between values from another thread C#

I want to use BackgroundWorker in my application. And I've learned, that when I want to do this:
buttonStart.Enabled = false;
in main thread, with another thread I should do it like this:
if (buttonStart.InvokeRequired) { buttonStart.Invoke(new Action(() => buttonStart.Enabled = false)); }
else buttonStart.Enabled = false;
But when it goes to comparision operations:
if(tabControlDbSwitch.SelectedIndex == 0)
it doesn't works.
So, here is my question:
if ((!tabControlDbSwitch.InvokeRequired && tabControlDbSwitch.SelectedIndex == 0) ||
(tabControlDbSwitch.InvokeRequired && /*What should I write here?*/))
And maybe you've got some hints for me, 'cause I'm totally newbie in multi threading, but I want to learn it as fast as possible.
I.e. I've heard that sometimes it will be better to use BeginInvoke than Invoke, but I don't know why and when.
CheckSelection is the function that you invoke from the function where this if code was return
public void CheckSelection()
{
if (tabControlDbSwitch.InvokeRequired)
{
tabControlDbSwitch.Invoke(new Action(() => { CheckTabSelection(); }));
}
else
CheckTabSelection();
}
public void CheckTabSelection()
{
if (tabControlDbSwitch.SelectedIndex == 0)
{
// Do my work .....
}
}
You said you have heard that sometimes it will be better to use BeginInvoke than Invoke, but I don't know why and when. invoke and begin invoke are of two types delegate and control. In your example you are using Contol.Invoke
Delegate.Invoke: Executes synchronously, on the same thread.
Delegate.BeginInvoke: Executes asynchronously, on a threadpool thread means the function that is invoked in begin invoke will be executed on a new thread from a thread pool and you can continue doing your operation on same thread (Parallel execution).
Control.Invoke: Executes on the UI thread, but calling thread will wait for completion of the invoked function before continuing.
Control.BeginInvoke: Executes on the UI thread, and calling thread will not wait for completion of invoked function.
Yes it is advisable that we use Control.BeginInvoke rather then Control.Invoke as you don't have to worry about deadlocks.
For example, if you remove the code
if(tabControlDbSwitch.InvokeRequired)
and always use
tabControlDbSwitch.Invoke(new Action(() => { CheckTabSelection(); }));
and in some case this function is invoked from UI main thread then your code will hang and result in deadlock.
Here's another approach that actually allows the Invoke() to RETURN a value.
This way your code flows a little better:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (GetSelectedIndex(this.tabControlDbSwitch) == 0)
{
Console.WriteLine("Success!");
}
}
private delegate int ReturnSelectedIndex(TabControl tb);
private int GetSelectedIndex(TabControl tb)
{
if (tb.InvokeRequired)
{
return (int)tb.Invoke(new ReturnSelectedIndex(GetSelectedIndex), new Object[] { tb });
}
else
{
return tb.SelectedIndex;
}
}

Form design messed up when using Show()

I'm trying to make a loading screen window. I use Show() instead of ShowDialog() because I have some code to execute after showing it. When using ShowDialog() form is fine but when using Show() form is messed up. What is causing this and what is the solution? Here is how I did it:
bool closeLoadingWindow = false;
void ShowLoadingWindow()
{
LoadingWindow loadingWindow = new LoadingWindow();
loadingWindow.Show();
while (!closeLoadingWindow);
loadingWindow.Close();
return;
}
public MainWindow()
{
Thread loadingWindowThread = new Thread(ShowLoadingWindow);
loadingWindowThread.Start();
InitializeComponent();
// ...
closeLoadingWindow = true;
}
When using ShowDialog():
When using Show():
The reason ShowDialog is working is because your while loop won't be executing, once the runtime hits that line of code it will stop processing until the form is dimissed.
Your code doesn't make sense, the point of using a thread here is to keep the "busy" code (your while loop) out of the main UI thread so it doesn't block. However, you are trying to create/show your form on the same thread, and a non-UI thread at that.
You don't necessarily need to use Show here, you can use ShowDialog but it is a little bit trickier in terms of dimissing the form etc. However, to solve the problem you have at the minute I would recommend you do:
LoadingWindow _loadingWindow;
void ShowLoadingWindow()
{
if (_loadingWindow == null)
_loadingWindow = new LoadingWindow();
_loadingWindow.Show();
}
void HideLoadingWindow()
{
if (_loadingWindow != null)
{
_loadingWindow.Close();
_loadingWindow.Dispose();
}
}
void LoadSomething()
{
while (...)
{
// busy code goes here
}
// after code is finished, close the form
MethodInvoker closeForm = delegate { HideLoadingWindow(); };
_loadingWindow.Invoke(closeForm);
}
public MainWindow()
{
ShowLoadingWindow();
new Thread(LoadSomething).Start();
}
}
FYI - Depending on the nature of exactly what your trying to do in the thread it might be a better approach to use the Task Parallel Library rather than creating a dedicated thread, various benefits like continuation / cancellation support.

GTK# Application.Invoke Not Working

I'm working on an application that has been tightly bound to GTK# by using Application.Invoke's through many of it's libraries. Unfortunately, we're porting the application (server type application) to a system with no window manager, so it currently crashed when we initialize GTK.
Application.Invoke doesn't seem to work without calling Application.Init, even when running my own GLib.MainLoop.
I am looking for a suitable replacement for Application.Invoke. How should I go about replacing Application.Invoke within the Libraries used by the application, so that I can remove the dependency on GTK ?
Note: I've proposed a refactoring to get rid of the GUI from the app and domain code and move it into a view, but that's been shot down for now. I'm basically trying to get it to run on a system with no window manager.
If it's async processing you want that doesn't need to happen on a specific thread, have a look at System.Threading.ThreadPool.QueueUserWorkItem. The main problem about this approach is that you have to ensure thread safety yourself.
If you do need it to happen on the main thread, you'll need to create a list of delegates to invoke and poll that list periodically on the main thread (or wait for something to be posted to it):
using System.Collections.Generic;
using System.Threading;
class Main {
Queue<Action> actions = new Queue<Action> ();
ManualResetEvent the_event = new ManualResetEvent (false);
public void Invoke (Action action)
{
lock (actions) {
actions.Enqueue (action);
the_event.Set ();
}
}
public void Poll ()
{
Action action = null;
lock (actions) {
if (actions.Count > 0) {
action = actions.Dequeue ();
}
}
if (action != null)
action ();
}
public void Wait ()
{
Action action = null;
while (true) {
the_event.WaitOne ();
lock (actions) {
if (actions.Count > 0) {
action = actions.Dequeue ();
} else {
the_event.Reset ();
}
}
if (action != null)
action ();
}
}
}
Application.Invoke basically works by keeping a list of delegates to run.
Each time the GTK main loop iterates it checks this list and executes anything it finds. It sounds like you need a background thread that loops like this.
That said, I can't imagine how or why you need this looped invoke on a non-graphical app, you would probably be just as well served by simply calling directly there and then. Eg:
public static class Application {
public static void Invoke ( EventHandler dothis ) {
if ( dothis != null ){
dothis( null, null ); }
}
}
Application.Invoke does not need to be replaced (at least for the version I am using). This was a misconception. Application.Inoke simply turns around and adds a delegate to GLib.Timeout, with the timeout set to 0, and returns "false", therefore only firing once.
Instead of getting rid of Application.Invoke, I attempted to find out why my delegates were not firing when using Application.Invoke without Appliation.Run or Application.Init. Keep in mind that I had already started my own GLib.MainLoop.
As it turns out, the Application's static constructor calls GLib.Thread.Init(), which is basically a timebomb. GLib's documentation states that GLib.Thread.Init must be called when using multiple threads, and that if GLib.Thread.Init is ever called, it must be called BEFORE any other GLib usage.
So, in the code I was working with, we added a delegate to GLib.Timeout after Application.Init, but before Application.Run, and before any calls to Application.Invoke. This means we were safe, because Application.Init would invoke the static constructor of Application, therefore invoking GLib.Thread.Init. This was good. However, when we removed Application.Init, and called Timeout.Add first, Thread.Init had not yet been called. This meant that if we called Thread.Init later, the threading, timeouts, delegates, etc, would choke.
Sure enough, Application.Invoke or Application.Run would invoke the static constructor of Application, which in turn would invoke GLib.Thread.Init. This caused the problem.
TLDR;
Long story short, make sure you invoke Application's static constructor before using Timeout.Add in your application code. Don't call Glib.Thread.Init manually, because calling it twice on Mono will make the application crash.
This is okay:
Application.Init();
Timeout.Add(0, delegate { return false; });
Application.Invoke(delegate { Console.WriteLine("Hey"); });
Application.Run();
This will ruin your life:
// Application.Init();
Timeout.Add(1000, delegate { return false; });
Application.Invoke(delegate { Console.WriteLine("Hey"); });
new MainLoop().Run();
//Application.Run();
But this is fine:
// Application.Init();
Application.Invoke(delegate {});
Timeout.Add(1000, delegate { return false; });
Application.Invoke(delegate { Console.WriteLine("Hey"); });
new MainLoop().Run();
//Application.Run();

Avoid calling Invoke when the control is disposed

I have the following code in my worker thread (ImageListView below is derived from Control):
if (mImageListView != null &&
mImageListView.IsHandleCreated &&
!mImageListView.IsDisposed)
{
if (mImageListView.InvokeRequired)
mImageListView.Invoke(
new RefreshDelegateInternal(mImageListView.RefreshInternal));
else
mImageListView.RefreshInternal();
}
However, I get an ObjectDisposedException sometimes with the Invoke method above. It appears that the control can be disposed between the time I check IsDisposed and I call Invoke. How can I avoid that?
What you have here is a race condition. You're better off just catching the ObjectDisposed exception and be done with it. In fact, I think in this case it is the only working solution.
try
{
if (mImageListView.InvokeRequired)
mImageListView.Invoke(new YourDelegate(thisMethod));
else
mImageListView.RefreshInternal();
}
catch (ObjectDisposedException ex)
{
// Do something clever
}
There are implicit race conditions in your code. The control can be disposed between your IsDisposed test and the InvokeRequired test. There's another one between InvokeRequired and Invoke(). You can't fix this without ensuring the control outlives the life of the thread. Given that your thread is generating data for a list view, it ought to stop running before the list view disappears.
Do so by setting e.Cancel in the FormClosing event and signaling the thread to stop with a ManualResetEvent. When the thread completes, call Form.Close() again. Using BackgroundWorker makes it easy to implement the thread completion logic, find sample code in this post.
The reality is that with Invoke and friends, you can't completely protect against invoke on a disposed component, or then getting InvalidOperationException because of the missing handle. I haven't really seen an answer yet, like the one farther below, in any of the threads that addresses the real fundamental problem, which cant be completely solved by preemptive testing or using lock semantics.
Here's the normal 'correct' idiom:
// the event handler. in this case preped for cross thread calls
void OnEventMyUpdate(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return; // ignore events if we arn't ready, and for
// invoke if cant listen to msg queue anyway
if (InvokeRequired)
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
else
this.MyUpdate(e.MyData);
}
// the update function
void MyUpdate(Object myData)
{
...
}
The fundemental problem:
In using the Invoke facility the windows message queue is used, which places a message in the queue to either wait or fire-and-forget the cross thread call exactly like Post or Send message. If there is a message ahead of the Invoke message that will invalidate the component and its window handle, or that got placed just after any checks you try to perform, then you are going to have a bad time.
x thread -> PostMessage(WM_CLOSE); // put 'WM_CLOSE' in queue
y thread -> this.IsHandleCreated // yes we have a valid handle
y thread -> this.Invoke(); // put 'Invoke' in queue
ui thread -> this.Destroy(); // Close processed, handle gone
y thread -> throw Invalid....() // 'Send' comes back, thrown on calling thread y
There is no real way to know that the control is about to remove itself fromthe queue, and nothing really reasonable you can do to "undo" the invoke. No matter how many checks you do or extra locks you make, you cant stop someone else form issuing something like a close, or deactivate. There are tons of senarios where this can happen.
A solution:
The first thing to realize is that the invoke is going to fail, no different than how a (IsHandleCreated) check would have ignored the event. If the goal is to protect the caller on the non-UI thread you will need to handle the exception, and treat it like any other call that didn't succeed (to keep app from crashing or do whatever. And unless going to rewrite/reroll Invoke facility, the catch is your only way to know.
// the event handler. in this case preped for cross thread calls
void OnEventMyWhatever(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return;
if (InvokeRequired)
{
try
{
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
}
catch (InvalidOperationException ex) // pump died before we were processed
{
if (this.IsHandleCreated) throw; // not the droids we are looking for
}
}
else
{
this.MyUpdate(e.MyData);
}
}
// the update function
void MyUpdate(Object myData)
{
...
}
The exception filtering can be tailored to suit whatever the needs are. Its good to be aware that worker threads often dont have all the cushy outer exception handling and logging the UI threads do, in most applicaitons, so you may wish to just gobble up any exception on the worker side. Or log and rethrow all of them. For many, uncaught exceptions on worker thread means the app is going to crash.
Try using
if(!myControl.Disposing)
; // invoke here
I had the exact same problem as you. Ever since I switched to checking .Disposing on the control, the ObjectDisposedException has gone away. Not saying this will fix it 100% of the time, just 99% ;) There is still a chance of a race condition between the check to Disposing and the call to invoke, but in the testing I've done I haven't ran into it (I use the ThreadPool and a worker thread).
Here's what I use before each call to invoke:
private bool IsControlValid(Control myControl)
{
if (myControl == null) return false;
if (myControl.IsDisposed) return false;
if (myControl.Disposing) return false;
if (!myControl.IsHandleCreated) return false;
if (AbortThread) return false; // the signal to the thread to stop processing
return true;
}
may be lock(mImageListView){...} ?
You could use mutexes.
Somewhere at the start of the thread :
Mutex m=new Mutex();
Then :
if (mImageListView != null &&
mImageListView.IsHandleCreated &&
!mImageListView.IsDisposed)
{
m.WaitOne();
if (mImageListView.InvokeRequired)
mImageListView.Invoke(
new RefreshDelegateInternal(mImageListView.RefreshInternal));
else
mImageListView.RefreshInternal();
m.ReleaseMutex();
}
And whereever it is you are disposing of mImageListView :
m.WaitOne();
mImageListView.Dispose();
m.ReleaseMutex();
This should ensure you cant dispose and invoke at the same time.
See also this question:
Avoiding the woes of Invoke/BeginInvoke in cross-thread WinForm event handling?
The utility class that resulted EventHandlerForControl can solve this problem for event method signatures. You could adapt this class or review the logic therein to solve the issue.
The real problem here is that nobugz is correct as he points out that the APIs given for cross-thread calls in winforms are inherently not thread safe. Even within the calls to InvokeRequired and Invoke/BeginInvoke themselves there are several race conditions that can cause unexpected behavior.
If a BackGroundWorker is a possibility, there's a very simple way to circumvent this:
public partial class MyForm : Form
{
private void InvokeViaBgw(Action action)
{
BGW.ReportProgress(0, action);
}
private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (this.IsDisposed) return; //You are on the UI thread now, so no race condition
var action = (Action)e.UserState;
action();
}
private private void BGW_DoWork(object sender, DoWorkEventArgs e)
{
//Sample usage:
this.InvokeViaBgw(() => MyTextBox.Text = "Foo");
}
}
Handle the Form closing event. Check to see if your off UI thread work is still happening, if so start to bring it down, cancel the closing event and then reschedule the close using BeginInvoke on the form control.
private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
if (service.IsRunning)
{
service.Exit();
e.Cancel = true;
this.BeginInvoke(new Action(() => { this.Close(); }));
}
}
The solution proposed by Isak Savo
try
{
myForm.Invoke(myForm.myDelegate, new Object[] { message });
}
catch (ObjectDisposedException)
{ //catch exception if the owner window is already closed
}
works in C# 4.0 but for some reasons it fails in C#3.0 (the exception is raised anyway)
So I used another solution based on a flag indicating if the form is closing and consequently preventing the use of invoke if the flag is set
public partial class Form1 : Form
{
bool _closing;
public bool closing { get { return _closing; } }
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_closing = true;
}
...
// part executing in another thread:
if (_owner.closing == false)
{ // the invoke is skipped if the form is closing
myForm.Invoke(myForm.myDelegate, new Object[] { message });
}
This has the advantage of completely avoiding the use of try/catch.
One way might be to call the method itself ones more instead of invoking the ImageListView-Method:
if (mImageListView != null &&
mImageListView.IsHandleCreated &&
!mImageListView.IsDisposed)
{
if (mImageListView.InvokeRequired)
mImageListView.Invoke(new YourDelegate(thisMethod));
else
mImageListView.RefreshInternal();
}
That way it would check one more time before finally calling RefreshInternal().
The suggestion to stop the thread generating the messages is not acceptable. Delegates can be multicast. Because one listener does not want to listen to the band, you don't shoot the band members.
Since the framework doesn't provide any easy way I know of to clear the message pump of those event messages, and since the form does not expose its private property that lets us know the form is closing:
Set a flag on the IsClosing Event of the window after you unsubscribe or stop listening to the events, and always check this flag before you do a this.Invoke().
i have same error. my error occurred in thread. finally i write this method :
public bool IsDisposed(Control ctrl)
{
if (ctrl.IsDisposed)
return true;
try
{
ctrl.Invoke(new Action(() => { }));
return false;
}
catch (ObjectDisposedException)
{
return true;
}
}
This works for me
if (this.IsHandleCreated){
Task.Delay(500).ContinueWith(_ =>{
this.Invoke(fm2);
});
} else {
this.Refresh();
}

Categories

Resources