初始屏幕在完成更新标签的所有步骤之前不会显示

本文关键字:有步骤 显示 标签 屏幕 更新 | 更新日期: 2023-09-27 18:35:03

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

初始屏幕在完成更新标签的所有步骤之前不会显示

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

您应该在主线程中处理初始屏幕,并在后台线程中处理初始化的后台工作(正如逻辑学家所评论的那样(。

也就是说,更改的消息未显示的原因是主线程繁忙,因此它不处理重绘控件的事件。在后台线程中调用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();
}

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

感谢大家回答我的问题。终于解决了我的问题!我更改了我的代码,以便现在在单独的线程上执行启动任务,并且从主屏幕(主(线程显示初始屏幕。在主屏幕中,我利用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);
    }

您必须在初始屏幕窗体中定义后台辅助角色。
下面是初始屏幕的外观示例:

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