直到更新标签中的所有步骤完成后才会显示启动画面

时间:2013-10-11 15:07:56

标签: c# .net multithreading winforms

我为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);
}

现在,直到其中的标签更新为“正在加载...”时,才会显示启动画面 请帮助让我知道我做错了什么。 我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();
}

修改
正如一些用户在这里建议的那样,我已将启动任务放在后台线程中,现在我正在从主线程中显示启动画面。但仍然存在同样的问题。以下是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;
}  

这是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 个答案:

答案 0 :(得分:1)

您想要BackgroundWorker发布ProgressChanged事件,这将更新启动画面。改进的进度对象可以是一个字符串,例如,您将在启动画面上显示(返回GUI线程)。

答案 1 :(得分:1)

您应该在主线程中处理启动画面,以及在后台线程中初始化的后台工作(就像logixologist所评论的那样)。

也就是说,更改的消息未显示的原因是主线程忙,因此它不处理重绘控件的事件。在后台线程中调用DoEvents将只处理该线程中的消息,并且更新启动屏幕的消息位于主线程中。

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

但是,这只是当前代码的一种解决方法。你应该让主线程处理消息。

答案 2 :(得分:1)

感谢大家回答我的问题。最后我的问题解决了!我更改了我的代码,使得启动任务现在在单独的线程上执行,并且从主屏幕(主)线程显示启动屏幕。在主屏幕中,我使用Backgroundworker更新了“正在加载...”标签 我在这里发布我的代码,希望它也可以帮助将来的某个人。
对于Home Screen代码,请参阅我的问题中的编辑部分。这是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);
    }

答案 3 :(得分:0)

您必须在初始屏幕表单中定义后台工作程序 以下是启动画面的示例:

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

你可以按照你想要的方式设计它,也许是一个漂亮的GIF图像,或者你能想到的任何东西。

这样称呼:

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..");

}