I have created a splash screen for a WinForm app. Everything works well until the splash screen is just a form with a background image and a label which has a static text - "Loading..."
I want to update the label continuously (with small delay in between) with texts - "Loading","Loading.","Loading.." and "Loading...". For this I have put the following code in my SplashScreen
form:
我为WinForm应用程序创建了一个启动画面。一切都很好,直到启动画面只是一个带有背景图像的表格和一个带有静态文本的标签 - “正在加载...”我想连续更新标签(两者之间有小延迟)和文本 - “正在加载” ,“正在加载。”,“正在加载......”和“正在加载...”。为此,我将以下代码放在SplashScreen表单中:
private void SplashScreen_Load(object sender, EventArgs e)
{
lblLoading.Refresh();
lblLoading.Text = "Loading.";
Thread.Sleep(500);
lblLoading.Refresh();
lblLoading.Text = "Loading..";
Thread.Sleep(500);
lblLoading.Refresh();
lblLoading.Text = "Loading...";
Thread.Sleep(500);
}
Now the Splash Screen doesn't appear until the label in it gets updated to "Loading..."
Please help to let me know what I am doing wrong. Code in my HomeScreen
:
现在直到其中的标签更新为“正在加载...”时才会出现启动画面。请帮助我告诉我我做错了什么。我的HomeScreen中的代码:
public HomeScreen()
{
//....
this.Load += new EventHandler(HandleFormLoad);
this.splashScreen = new SplashScreen();
}
private void HandleFormLoad(object sender, EventArgs e)
{
this.Hide();
Thread thread = new Thread(new ThreadStart(this.ShowSplashScreen));
thread.Start();
//...Performing tasks to be done while splash screen is displayed
done = true;
this.Show();
}
private void ShowSplashScreen()
{
splashScreen.Show();
while (!done)
{
Application.DoEvents();
}
splashScreen.Close();
this.splashScreen.Dispose();
}
EDIT:
As suggested by some users here I have put the startup tasks in background thread and now I am displaying the Splash Screen from the main thread. But still the same issue persist. Here is the updated code of HomeScreen
form:
编辑:正如一些用户在这里所建议的那样,我已经将启动任务放在后台线程中,现在我正在从主线程中显示启动画面。但仍然存在同样的问题。以下是HomeScreen表单的更新代码:
public HomeScreen()
{
//...
this.Load += new EventHandler(HandleFormLoad);
}
private void HandleFormLoad(object sender, EventArgs e)
{
this.Hide();
SplashScreen sc = new SplashScreen();
sc.Show();
Thread thread = new Thread(new ThreadStart(PerformStartupTasks));
thread.Start();
while (!done)
{
Application.DoEvents();
}
sc.Close();
sc.Dispose();
this.Show();
}
private void PerformStartupTasks()
{
//..perform tasks
done = true;
}
and here's the Splash Screen
:
这是启动画面:
private void SplashScreen_Load(object sender, EventArgs e)
{
lblLoading.Update();
lblLoading.Text = "Loading.";
Thread.Sleep(500);
lblLoading.Update();
lblLoading.Text = "Loading..";
Thread.Sleep(500);
lblLoading.Update();
lblLoading.Text = "Loading...";
Thread.Sleep(500);
}
4 个解决方案
#1
1
You want a BackgroundWorker that posts ProgressChanged event, which will update the splash screen. The progress changed object could be a string, for example, that you'll display on your splash screen (back on the GUI thread).
您需要一个发布ProgressChanged事件的BackgroundWorker,它将更新启动屏幕。改进的进度对象可以是一个字符串,例如,您将在启动画面上显示(返回GUI线程)。
#2
1
You should handle the splash screen in the main thread, and the background work of initialising in the background thread (just as logixologist commented).
您应该在主线程中处理启动画面,并在后台线程中初始化后台工作(就像logixologist所评论的那样)。
That said, the reason that your changed message doesn't show up is because the main thread is busy so it doesn't handle the events that redraws the control. Calling DoEvents
in a background thread will only handle messages in that thread, and the messages to update the splash screen is in the main thread.
也就是说,更改的消息未显示的原因是主线程忙,因此它不处理重绘控件的事件。在后台线程中调用DoEvents仅处理该线程中的消息,并且更新启动屏幕的消息位于主线程中。
The Refresh
method only invalidates the control, which would cause it to be redrawn if the main thread handled the event. You can use the Update
method to force the redraw of the control:
Refresh方法仅使控件无效,如果主线程处理事件,则会导致重绘。您可以使用Update方法强制重绘控件:
private void SplashScreen_Load(object sender, EventArgs e)
{
lblLoading.Text = "Loading.";
lblLoading.Update();
Thread.Sleep(500);
lblLoading.Text = "Loading..";
lblLoading.Update();
Thread.Sleep(500);
lblLoading.Text = "Loading...";
lblLoading.Update();
}
But, this is only a workaround for the current code. You should really make the main thread handle the messages.
但是,这只是当前代码的一种解决方法。你应该让主线程处理消息。
#3
1
Thanks to all for answering my questions. Finally my issue is solved ! I changed my code such that start-up tasks are now being performed on a separate thread and splash screen is displayed from the Home Screen (main) thread. In the home screen I made use of Backgroundworker
to update the 'Loading...' label.
I am posting my code here hoping it may also help someone in future..
For the Home Screen
code plz see the EDIT part in my question. Here's the code of Splash Screen
:
感谢大家回答我的问题。最后我的问题解决了!我更改了我的代码,使得启动任务现在在单独的线程上执行,并且从主屏幕(主)线程显示启动屏幕。在主屏幕中,我使用Backgroundworker更新了“Loading ...”标签。我在这里发布我的代码希望它也可能在将来帮助某人。对于主屏幕代码,请参阅我的问题中的编辑部分。这是Splash Screen的代码:
public SplashScreen()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = false;
lblLoading.Text = string.Empty;
}
private void SplashScreen_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (true)
{
for (int i = 1; i <= 20; i++)
{
System.Threading.Thread.Sleep(200);
worker.ReportProgress(i);
}
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string dots = string.Empty;
for (int k = 1; k <= e.ProgressPercentage; k++)
{
dots = string.Format("{0}..",dots);
}
lblLoading.Text = ("Loading" + dots);
}
#4
0
You have to define a background worker in your splash screen form.
Here is an example of what your splash screen could look like :
您必须在初始屏幕窗体中定义后台工作程序。以下是启动画面的示例:
public partial class SplashScreenForm<T> : Form
{
private BackgroundWorker _backgroundWorker;
private Func<BackgroundWorker, T> _func;
private T _funcResult;
public T FuncResult { get { return _funcResult; } }
public SplashScreenForm(Func<BackgroundWorker, T> func)
{
this._func = func;
InitializeComponent();
this.label1.Text = "";
this._backgroundWorker = new BackgroundWorker();
this._backgroundWorker.WorkerReportsProgress = true;
this._backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
this._backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);
this._backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
_backgroundWorker.RunWorkerAsync();
}
private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
if (worker != null)
{
_funcResult = this._func.Invoke(worker);
}
}
private void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.UserState != null)
{
this.label1.Text = e.UserState.ToString();
}
}
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Close();
}
}
You can design it the way you want, maybe a pretty gif image, or whatever you can think of.
你可以按照你想要的方式设计它,也许是一个漂亮的GIF图像,或者你能想到的任何东西。
Call it this way :
这样称呼它:
private void HandleFormLoad(object sender, EventArgs e)
{
this.Hide();
var splash = new SplashScreenForm<bool>(PerformTask);
splash.ShowDialog(); // function PerformTask will be launch at this moment
this.Show();
}
private bool PerformTask(BackgroundWorker worker)
{
//...Performing tasks to be done while splash screen is displayed
worker.ReportProgress("loading..");
}
#1
1
You want a BackgroundWorker that posts ProgressChanged event, which will update the splash screen. The progress changed object could be a string, for example, that you'll display on your splash screen (back on the GUI thread).
您需要一个发布ProgressChanged事件的BackgroundWorker,它将更新启动屏幕。改进的进度对象可以是一个字符串,例如,您将在启动画面上显示(返回GUI线程)。
#2
1
You should handle the splash screen in the main thread, and the background work of initialising in the background thread (just as logixologist commented).
您应该在主线程中处理启动画面,并在后台线程中初始化后台工作(就像logixologist所评论的那样)。
That said, the reason that your changed message doesn't show up is because the main thread is busy so it doesn't handle the events that redraws the control. Calling DoEvents
in a background thread will only handle messages in that thread, and the messages to update the splash screen is in the main thread.
也就是说,更改的消息未显示的原因是主线程忙,因此它不处理重绘控件的事件。在后台线程中调用DoEvents仅处理该线程中的消息,并且更新启动屏幕的消息位于主线程中。
The Refresh
method only invalidates the control, which would cause it to be redrawn if the main thread handled the event. You can use the Update
method to force the redraw of the control:
Refresh方法仅使控件无效,如果主线程处理事件,则会导致重绘。您可以使用Update方法强制重绘控件:
private void SplashScreen_Load(object sender, EventArgs e)
{
lblLoading.Text = "Loading.";
lblLoading.Update();
Thread.Sleep(500);
lblLoading.Text = "Loading..";
lblLoading.Update();
Thread.Sleep(500);
lblLoading.Text = "Loading...";
lblLoading.Update();
}
But, this is only a workaround for the current code. You should really make the main thread handle the messages.
但是,这只是当前代码的一种解决方法。你应该让主线程处理消息。
#3
1
Thanks to all for answering my questions. Finally my issue is solved ! I changed my code such that start-up tasks are now being performed on a separate thread and splash screen is displayed from the Home Screen (main) thread. In the home screen I made use of Backgroundworker
to update the 'Loading...' label.
I am posting my code here hoping it may also help someone in future..
For the Home Screen
code plz see the EDIT part in my question. Here's the code of Splash Screen
:
感谢大家回答我的问题。最后我的问题解决了!我更改了我的代码,使得启动任务现在在单独的线程上执行,并且从主屏幕(主)线程显示启动屏幕。在主屏幕中,我使用Backgroundworker更新了“Loading ...”标签。我在这里发布我的代码希望它也可能在将来帮助某人。对于主屏幕代码,请参阅我的问题中的编辑部分。这是Splash Screen的代码:
public SplashScreen()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = false;
lblLoading.Text = string.Empty;
}
private void SplashScreen_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (true)
{
for (int i = 1; i <= 20; i++)
{
System.Threading.Thread.Sleep(200);
worker.ReportProgress(i);
}
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string dots = string.Empty;
for (int k = 1; k <= e.ProgressPercentage; k++)
{
dots = string.Format("{0}..",dots);
}
lblLoading.Text = ("Loading" + dots);
}
#4
0
You have to define a background worker in your splash screen form.
Here is an example of what your splash screen could look like :
您必须在初始屏幕窗体中定义后台工作程序。以下是启动画面的示例:
public partial class SplashScreenForm<T> : Form
{
private BackgroundWorker _backgroundWorker;
private Func<BackgroundWorker, T> _func;
private T _funcResult;
public T FuncResult { get { return _funcResult; } }
public SplashScreenForm(Func<BackgroundWorker, T> func)
{
this._func = func;
InitializeComponent();
this.label1.Text = "";
this._backgroundWorker = new BackgroundWorker();
this._backgroundWorker.WorkerReportsProgress = true;
this._backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
this._backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);
this._backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
_backgroundWorker.RunWorkerAsync();
}
private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
if (worker != null)
{
_funcResult = this._func.Invoke(worker);
}
}
private void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.UserState != null)
{
this.label1.Text = e.UserState.ToString();
}
}
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Close();
}
}
You can design it the way you want, maybe a pretty gif image, or whatever you can think of.
你可以按照你想要的方式设计它,也许是一个漂亮的GIF图像,或者你能想到的任何东西。
Call it this way :
这样称呼它:
private void HandleFormLoad(object sender, EventArgs e)
{
this.Hide();
var splash = new SplashScreenForm<bool>(PerformTask);
splash.ShowDialog(); // function PerformTask will be launch at this moment
this.Show();
}
private bool PerformTask(BackgroundWorker worker)
{
//...Performing tasks to be done while splash screen is displayed
worker.ReportProgress("loading..");
}