显示表单时发生ThreadState异常

时间:2011-02-09 12:31:13

标签: c# winforms

我有一个表单,需要几秒钟才能加载。所以,我想用“正在加载,请等待”的文字显示一个小表格。表格加载完毕后,必须关闭装货单。

所以,我创建了一个简单的类,它在一个线程中显示一个加载表单:

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();
    }
}

在主要表格中我有:

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

BUT。在这段代码之后,我在主窗体上做了一些事情:this.Opacity = 0;。此时,我可以在调试器中看到线程停止工作并抛出ThreadStateException并且加载表单消失了。

这是为什么?

6 个答案:

答案 0 :(得分:1)

这是您展示简单的启动画面(表单)的方法。你如何设计它取决于你(我更喜欢一个图片框和没有面板的形式 - 看起来不错,非常简单))。

public partial class MainForm : Form
{
    public MainForm()
    {
        SplashForm sf = new SplashForm();
        sf.Show();

        InitializeComponent();

        this.SuspendLayout();
        // Do the positioning...
        this.ResumeLayout();

        sf.Close();
    }
}

答案 1 :(得分:1)

您的程序遭到炸弹,因为您中止了线程,但没有关注窗口。由于Windows通知,它可能会尝试运行代码,因为线程被中止,因此会在ThreadStateException上急转直下。你无法通过中止来结束线程。

这是一个解决这个问题的通用类,它负责干净地关闭等待表单和线程。

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;
    }

}

样本用法:

    private void button1_Click(object sender, EventArgs e) {
        using (new Loader(this, new LoadingForm())) {
            System.Threading.Thread.Sleep(3000);
        }
    }

答案 2 :(得分:0)

我相信你不应该从后台线程创建和显示表单,应该从主UI线程操作UI,然后你可以让工作线程执行一些后台任务,但不能创建和显示表单。

答案 3 :(得分:0)

答案 4 :(得分:0)

如果您的问题是表单闪烁,那么在表单加载中使用此代码,使用双缓冲来隐藏闪烁

this.SetStyle(ControlStyles.DoubleBuffer | 
      ControlStyles.UserPaint | 
      ControlStyles.AllPaintingInWmPaint,
      true);
   this.UpdateStyles();

答案 5 :(得分:0)

我正在做你想要的,我可以向你展示我的线程功能 - 所以你可以得出你最终的错误 - 我可以在几十台部署的机器上开发和生产。

    static void ThreadFunc()
    {
        _splash = new Splash();
        _splash.Show();
        while (!_shouldClose)
        {
            Application.DoEvents();
            Thread.Sleep(100);
            if (new Random().Next(1000) < 10)
            {
                _splash.Invoke(new MethodInvoker(_splash.RandomizeText));
            }
        }
        for (int n = 0; n < 18; n++)
        {
            Application.DoEvents();
            Thread.Sleep(60);
        }
        if (_splash != null)
        {
            _splash.Close();
            _splash = null;
        }
    }

Splash这是我的表单 - 启动画面。我有这个:

    static public void ShowSplash()
    {
        _shouldClose = false;
        Thread t = new Thread(ThreadFunc);
        t.Priority = ThreadPriority.Lowest;
        t.Start();
    }

我在我的Program.Main()之前调用,然后显示应用程序的主要形式。应用程序正常加载,更新主屏幕,完成所有处理后,我使用:

    internal static void RemoveSplash()
    {
        _shouldClose = true;
    }