0

我为 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

4 回答 4

1

您需要一个发布ProgressChanged事件的BackgroundWorker ,该事件将更新初始屏幕。例如,进度更改对象可以是一个字符串,您将显示在初始屏幕上(返回到 GUI 线程上)。

于 2013-10-11T15:13:11.273 回答
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();
}

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

于 2013-10-11T15:21:34.400 回答
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);
    }
于 2013-10-11T19:12:40.513 回答
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..");

}
于 2013-10-11T15:23:19.223 回答