我有一个表单,需要几秒钟才能加载。所以,我想用“正在加载,请等待”的文字显示一个小表格。表格加载完毕后,必须关闭装货单。
所以,我创建了一个简单的类,它在一个线程中显示一个加载表单:
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
并且加载表单消失了。
这是为什么?
答案 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;
}