0

I have used Thread to view a waiting form while my main form is loading in the Load event. But sometimes I get an ThreadAbortException that says "Thread was being aborted.".

Please help me to fix this problem.

Main Form Load event code:

WaitingForm wf;

MainForm_Load(object sender, EventArgs e)
{
        Thread t = new Thread(new ThreadStart(delegate
        {
            wf = new WaitingForm();
            wf.ShowDialog();
        }));
        t.Start();

        /***** Do Something *****/

        if (wf != null)
            wf.closeLoad();
        t.Abort();
}

WaitingForm CloseLoad Function:

public void closeLoad()
{
    Invoke((MethodInvoker)delegate
    {
        this.Close();
    });
}
Kzryzstof
  • 7,688
  • 10
  • 61
  • 108
Seena Fallah
  • 43
  • 1
  • 6
  • 1
    You're creating a new form on a background thread. All UI work should be done on the UI thread. A better solution would be to open the WaitingForm on the UI thread then in the Load event of the WaitingForm start a BackgroundWorker process to do your long running operation, then close the WaitingForm. – Handbag Crab Dec 02 '18 at 16:05
  • 1
    What is `rl`? Is it the same as `wf`? – Klaus Gütter Dec 02 '18 at 16:07
  • 2
    Avoid `Abort`. Period. I am not sure what you want yo do... however, you might be interested in `ManualResetEventSlim`, `Close` and `BeginInvoke`. What is `rl`? Addendum 1: Using `async/await`, `Task`, or `BackgroundWorker` are probably good ideas. Addendum 2: Ah, did you want `Application.Run`? – Theraot Dec 02 '18 at 16:07
  • Not the answer to your question, but try method `Show()` without a new thread instead of `ShowDialog()` in a new thread.. – Dialecticus Dec 02 '18 at 16:08
  • 1
    Possible duplicate of [Load Data and show a splash screen during that](https://stackoverflow.com/questions/1489325/load-data-and-show-a-splash-screen-during-that) – Theraot Dec 02 '18 at 16:13
  • @HandbagCrab It would be but its filling the datagridview that takes while to complete I can't do this action before the form open! Any solution for this? – Seena Fallah Dec 02 '18 at 16:15
  • @KlausGütter Yes sorry for this mistake. It gets edit. – Seena Fallah Dec 02 '18 at 16:16
  • @Theraot My loading form progress takes while so I want to show a waitingform (included a simple gif) while the form is loading. Its filling my datagridview and it took a while for filling that. Can you give an example of async/await, Task or backgroundworker examples please? – Seena Fallah Dec 02 '18 at 16:18
  • Load the data to a `BindingList` or a `DataTable` in a background thread then once the data is loaded and the thread has complete you bind the data to the datagridview. – Handbag Crab Dec 02 '18 at 16:26
  • Possible duplicate of [Thread abort exception](https://stackoverflow.com/questions/2213343/thread-abort-exception) – Jay Dec 07 '18 at 19:28

1 Answers1

0

This is as compact as I could get it:

var waitingForm = new WaitingForm();
(new Thread (() => Application.Run(waitingForm))).Start();
HeavyWork();
waitingForm.BeginInvoke(new Action(() => waitingForm.Close()));

waitingForm will be fully responsive, operating on the anonymous thread with its own message loop. However, since it is running on another thread, we need BeginInvoke to close it.

We can swap this fire and forget thread with a Task:

var waitingForm = new WaitingForm();
Task.Run(() => Application.Run(waitingForm));
HeavyWork();
waitingForm.BeginInvoke((Action)(() => waitingForm.Close()));

For an alternative that uses async, consinder the following:

var source = new TaskCompletionSource<int>();

Enabled = false;
ShowWaitForm();
await HeavyWorkAsync();
Enabled = true;
source.SetResult(0);

async void ShowWaitForm()
{
    var waitingForm = new WaitingForm();
    waitingForm.Show(this);
    await source.Task;
    waitingForm.Close();
}

This time we are using TaskCompletionSource to notify that the work is finished and the WaitingForm can be closed, in such way that we can await it.

There is a difference that the user will percieve: The MainForm will be visible, and could even be responsive... we solve this by disabling it and making the WaitingForm a child form.

Another difference is that in this case the WaitingForm does not have its own message loop. However, it would still be responsive as long as you are using an async method to do your work.

Remember, it is a single thread handling both forms. This might or might not be convenient.

You can always fall back to use Task.Run:

var source = new TaskCompletionSource<int>();

Enabled = false;
ShowWaitForm();
await Task.Run(() => HeavyWork());
Enabled = true;
source.SetResult(0);

async void ShowWaitForm()
{
    var waitingForm = new WaitingForm();
    waitingForm.Show(this);
    await source.Task;
    waitingForm.Close();
}

Note that when using Task.Run on HeavyWork, HeavyWork is the part running in another thread. Now, it would need BeginInvoke to interact with the MainForm.

From there we can easily go to BackgroundWorker:

var source = new TaskCompletionSource<int>();

Enabled = false;
ShowWaitForm();
var bg = new BackgroundWorker();
bg.DoWork += (_, __) => HeavyWork();
bg.RunWorkerCompleted += (_, __) =>
{
    Enabled = true;
    source.SetResult(0);
};
bg.RunWorkerAsync();


async void ShowWaitForm()
{
    var waitingForm = new WaitingForm();
    waitingForm.Show(this);
    await source.Task;
    waitingForm.Close();
}
Theraot
  • 31,890
  • 5
  • 57
  • 86