【问题标题】:Splash Screen not showing up until all the steps of updating label in it is complete在更新标签的所有步骤完成之前,不会显示启动画面
【发布时间】:2013-10-11 15:07:56
【问题描述】:

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

【问题讨论】:

  • 你不要把它倒过来..启动画面需要在主线程中,加载需要在后台......看看是否有帮助。
  • 你应该了解ManualResetEventwhile(!done){} 很苛刻...
  • @logixologist 我尝试按照您的建议进行操作。仍然没有帮助。请看我的编辑

标签: c# .net multithreading winforms


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    您应该在主线程中处理启动画面,以及在后台线程中进行初始化的后台工作(正如逻辑学家评论的那样)。

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

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

    【讨论】:

    • 我尝试按照您的建议进行操作。但问题仍未解决。请看看我的编辑。
    • @Luftwaffe:当您使控件无效时,在更改控件属性之前或之后执行此操作都没有关系,因为在您退出方法之前不会发生更新控件的事件,但是当您使用Update 时,您需要在更改控件属性后执行此操作,因为它会立即使用当前值重绘控件。
    • 我不知道这一点。非常感谢您告诉我。
    【解决方案3】:

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

    【讨论】:

    • 解决这个问题的更简单的方法是在启动画面表单上设置一个计时器,它可以在它的 Tick 事件中更新标签..
    • 如果这是您问题的解决方案,您应该接受您的回答,以便其他看到此问题的人知道:)
    【解决方案4】:

    您必须在初始屏幕表单中定义一个后台工作人员。
    以下是您的初始屏幕的示例:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多