我有一个Windows窗体应用程序,需要在加载主窗口之前加载一堆东西。我认为这可以证明ProgressBar
是合理的,所以我想我使用我的主窗体的构造函数显示另一个包含ProgressBar
Control
的表单。
一切正常但如果我尝试将文本放在简介形式的Label
中,则在加载主窗体之前,其内容将不会显示。除了首先加载介绍窗口之外,这里有一种方法可以避免这种情况吗?
答案 0 :(得分:5)
警告:此帖子包含自我推广的元素; o)
在这种情况下,我可能会使用初始形式。我前一段时间写过a blog post(由this SO Q&A触发)关于一个可以一起使用的线程安全的初始化表单将长期运行的主表单初始化。
简而言之,方法是使用ShowDialog,但要在单独的线程上创建和显示表单,这样它就不会阻塞主线程。表单包含状态消息标签(当然也可以使用进度条进行扩展)。然后有一个静态类,它提供了用于显示,更新和关闭splash表单的线程安全方法。
简明代码示例(对于已注释的代码示例,请查看博客文章):
using System;
using System.Windows.Forms;
public interface ISplashForm
{
IAsyncResult BeginInvoke(Delegate method);
DialogResult ShowDialog();
void Close();
void SetStatusText(string text);
}
using System.Windows.Forms;
public partial class SplashForm : Form, ISplashForm
{
public SplashForm()
{
InitializeComponent();
}
public void SetStatusText(string text)
{
_statusText.Text = text;
}
}
using System;
using System.Windows.Forms;
using System.Threading;
public static class SplashUtility<T> where T : ISplashForm
{
private static T _splash = default(T);
public static void Show()
{
ThreadPool.QueueUserWorkItem((WaitCallback)delegate
{
_splash = Activator.CreateInstance<T>();
_splash.ShowDialog();
});
}
public static void Close()
{
if (_splash != null)
{
_splash.BeginInvoke((MethodInvoker)delegate { _splash.Close(); });
}
}
public static void SetStatusText(string text)
{
if (_splash != null)
{
_splash.BeginInvoke((MethodInvoker)delegate { _splash.SetStatusText(text); });
}
}
}
使用示例:
SplashUtility<SplashForm>.Show();
SplashUtility<SplashForm>.SetStatusText("Working really hard...");
SplashUtility<SplashForm>.Close();
答案 1 :(得分:2)
BackgroundWorker
。
以下是来自Figo Fei的代码段,稍作修改以便进行说明:
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Maximum = 100;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// This would be the load process, where you put your Load methods into.
// You would report progress as something loads.
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
backgroundWorker1.ReportProgress(i); //run in back thread
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) //call back method
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) //call back method
{
progressBar1.Value = progressBar1.Maximum;
}
希望这会对你有所帮助。
答案 2 :(得分:1)
您可以从主程序或MainForm构造函数中显示您的SplashForm,这并不重要。您所看到的是,只要您的加载过程未完成,就不会处理任何消息,因此不会发生屏幕更新。 ProgressBar是一个例外,它正是因为这个原因而运行它自己的线程。
简短的解决方案是在更改标签后执行SplashForm.Update()
。更多涉及的是使用MessagePump(Application.Run)启动单独的线程。 Here is a SO question还有更多潜在客户。
答案 3 :(得分:0)
问题很可能是因为在您尝试显示进度条表单时没有正在运行的消息循环。在应用程序的入口点应该有一行代码如下所示。
Application.Run(new Form1());
对Application.Run的调用将启动消息循环,但您是否看到在消息循环运行之前如何执行Form1构造函数?由于你的进度条逻辑在那个构造函数中,因此没有运行的机制可以调度表单的绘制消息。
我认为最好的方法是首先加载启动画面,然后启动工作线程(你可以使用BackgroundWorker)来完成耗时的工作。进度条将显示在初始屏幕上,您将定期更新。工作完成后,您可以关闭启动画面并加载主窗体。