Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreadState exception occurs when showing a form

Tags:

c#

winforms

I have a form which take a few seconds to load. So therefore, I want to show a little form with the text 'Loading, please wait'. When the form is finished loading, the loading form must be closed.

So, I made a simple class which Shows a loading form in a thread:

public class ShowLoadingForm
{
    Thread _thread;

    public void Show()
    {
        try
        {
            _thread = new Thread(new ThreadStart(ShowForm));
            _thread.SetApartmentState(ApartmentState.STA);
            _thread.IsBackground = true;
            _thread.Start();
        }
        catch (Exception ex)
        {
            ErrorMessages.UnknownError(true, ex);
        }
    }

    private void ShowForm()
    {
        LoadingForm f = new LoadingForm();
        f.TopMost = true;
        f.ShowInTaskbar = true;
        f.SetText(" Loading... ");
        f.Show();
    }

    public void Close()
    {
        _thread.Abort();
    }
}

In the main form I have:

_loadingForm = new ShowLoadingForm();
_loadingForm.Show();

BUT. After this piece of code, I do something on the main form: this.Opacity = 0;. At this point, I can see in the debugger that the thread is stopped working and a ThreadStateException is thrown and the loading form is disappeared.

Why is this?

like image 378
Martijn Avatar asked Feb 21 '26 05:02

Martijn


1 Answers

Your program bombs because you abort the thread but do not take care of the window. It is liable to try to run code because of a Windows notification, that will nosedive on ThreadStateException because the thread is aborted. You simply cannot end the thread by aborting it.

Here's a general class to solve this problem, it takes care of shutting down the waiting form and the thread cleanly.

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

class Loader : IDisposable {
    private AutoResetEvent initialized = new AutoResetEvent(false);
    private Form loadForm;
    private Rectangle ownerRect;
    private bool closeOkay;

    public Loader(Form owner, Form pleaseWait) {
        if (pleaseWait.IsDisposed) throw new InvalidOperationException("Create a *new* form instance");
        loadForm = pleaseWait;
        loadForm.TopMost = true;
        loadForm.ShowInTaskbar = false;
        loadForm.StartPosition = FormStartPosition.Manual;
        ownerRect = new Rectangle(owner.Location, owner.Size);
        loadForm.Load += delegate {
            loadForm.Location = new Point(
                ownerRect.Left + (ownerRect.Width - loadForm.Width) / 2,
                ownerRect.Top + (ownerRect.Height - loadForm.Height) / 2);
            initialized.Set();
        };
        loadForm.FormClosing += new FormClosingEventHandler((s, ea) => {
            ea.Cancel = !closeOkay;
        });
        var t = new Thread(() => {
            Application.Run(loadForm);
        });
        t.SetApartmentState(ApartmentState.STA);
        t.IsBackground = true;
        t.Start();
        initialized.WaitOne();
    }

    public void Dispose() {
        if (loadForm == null) throw new InvalidOperationException();
        loadForm.Invoke((MethodInvoker)delegate {
            closeOkay = true;
            loadForm.Close(); 
        });
        loadForm = null;
    }

}

Sample usage:

    private void button1_Click(object sender, EventArgs e) {
        using (new Loader(this, new LoadingForm())) {
            System.Threading.Thread.Sleep(3000);
        }
    }
like image 94
Hans Passant Avatar answered Feb 23 '26 18:02

Hans Passant