2010-08-13 148 views
4

启动画面的概念并不会让我觉得应该如此复杂,但我无法完成整个启动画面的绘制。制作启动画面

假设我有两个System.Windows.Forms.Form s:RealForm和SplashScreen。 RealForm包含最初的GUI。在RealForm加载过程中(即在Load事件的事件处理程序中),RealForm创建到数据库的连接,然后测试连接。这可能需要几秒钟,所以我做的这一切之前,我试着投了闪屏,像这样:

private void RealForm_Load(object sender, EventArgs e) 
{ 
    SplashScreen splash = new SplashScreen(); 
    splash.Show(); 
    loadAndCheckDatabase(); 
    splash.Close(); 
} 

的问题是,闪屏仅被部分地拉伸,所以它并没有真正看起来很像一个闪屏。任何想法为什么?我该怎么办?

对于奖励积分,有没有人知道在哪里可以找到所有发生在典型的创建,使用和破坏形式的事件的解释?上面的问题可能是因为我不明白什么顺序的事情发生或挂钩到表单事件。


UPDATE 关闭,但并不完全:寻找多一点的建议。下面是当前的代码:

private SplashScreen splash = new SplashScreen(); 

private void RealForm_Load(object sender, EventArgs e) 
{ 

    splash.Show(); 

    BackgroundWorker worker = new BackgroundWorker(); 
    worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler 
     (worker_RunWorkerCompleted); 
    worker.RunWorkerAsync(); 
    this.Visible = false; 

} 

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    splash.Close(); 
    this.Visible = true; 
} 

void worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    loadAndCheckDatabase(); 
} 

不幸的是,this.Visible = false没有做任何事情,因为它是在代码中,我没有控制自动设置为true。所以,为了解决这个问题,我把this.Visible = false放到表单的Shown事件的处理程序中。但是,正如你可能猜到的那样,你仍然可以在瞬间看到表格......所以这不是一个很好的解决方案。

有什么办法可以在工作线程上等待,而我仍然在Load处理程序中?

回答

9

您正在显示启动画面并在同一个线程上检查数据库。线程一次只能做一件事。

解决此问题的一种快速且便宜的方法是定期致电loadAndCheckDatabase()致电Application.DoEvents()。然而,这是一个便宜的修复。

你真的想在自己的线程上运行loadAndCheckDatabase(),一个BackgroundWorker是一个很好的简单的方法来做到这一点。

+2

这可能很明显,但请确保在loadAndCheckDatabase调用中没有任何UI代码,甚至没有消息框,否则可能会遇到问题。 – 2010-08-13 16:45:32

+2

我甚至不打扰Application.DoEvents(),只是使用BackgroundWorker。如果您想在UI保持响应时执行一个简单的逻辑单元,请使用BackgroundWorker。如果您希望UI线程去执行在UI的消息队列中等待的所有内容,* even *如果用户设法排队等待鼠标点击,这些点击会执行看起来像线程负载正在运行,但实际上只有一个,然后使用Application.DoEvents()。 – 2010-08-13 18:00:08

1

尝试在后台线程中调用loadAndCheckDatabase,将启动屏幕关闭移动到那里,或者直接在启动屏幕中用计时器关闭它。通过在后台线程中工作,所有UI功能都可以在不中断的情况下运行。

1

您应该在不同的线程中运行启动画面,以便它能够正确绘制。

看一看这里:

http://www.codeproject.com/KB/cs/prettygoodsplashscreen.aspx

+2

由于启动画面是一个UI组件,它确实应该在UI线程(WinForms应用程序的主线程)上运行。 loadAndCheckDatabase()应该推送到工作线程。 – 2010-08-13 16:22:41

+0

UI组件实际上不能也不应该在后台线程上运行 - 它们只能在主线程上运行。 – Russ 2010-08-13 16:38:55

+0

没错,在后台线程上没有UI,你会得到一堆交叉线程调用异常。 – 2010-08-13 16:43:57

-2

可以使用的await命令与.Net框架4。直到任务完成

private void YourForm_Load(object sender, EventArgs e) 
    { 
     //call SplashScreen form 
     SplashScreen splash = new SplashScreen(); 
     splash.Show(); 
     Application.DoEvents(); 
     //Run your long task while splash screen is displayed i.e. loadAndCheckDatabase 
     Task processLongTask = loadAndCheckDatabase(); 
     //wait for the task to be completed 
     processLongTask.Wait(); 
     splash.Close(); 

     //... 
    } 
+2

上述代码绝对没有优势,因为主线程会立即阻止等待任务完成。你也可以在主线程中做长时间的任务,它没有任何区别。 – Mark 2013-08-15 11:16:19

1

为什么不是当你运行应用程序打开任何你需要加载到一个类加载了一个表单,那么当它完成加载,打开你的主5 您的形式将不可见形式并将其发送到它?或者,您可以使用单例来加载所有内容。

在你的Program.cs:

[STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 
     Application.Run(new SplashScreen()); 
    } 

然后在闪屏:

public SplashScreen() 
    { 
     InitializeComponent(); 
     LoadEverything(); 
     this.Visible = false; 
     MainForm mainForm = new MainForm(LoadedClass); 
     mainForm.ShowDialog(); 
     this.Close(); 
    } 

我需要更新此:

这里的工作代码(以上只是概念)。

public SplashScreen() 
    { 
     InitializeComponent(); 
     _currentDispatcher = Dispatcher.CurrentDispatcher; 

     // This is just for the example - start a background method here to call 
     // the LoadMainForm rather than the timer elapsed 
     System.Timers.Timer loadTimer = new System.Timers.Timer(2000); 
     loadTimer.Elapsed += LoadTimerElapsed; 
     loadTimer.Start(); 
    } 

    public void LoadMainForm() 
    { 
     // Do your loading here 
     MainForm mainForm = new MainForm(); 

     Visible = false; 
     mainForm.ShowDialog(); 
     System.Timers.Timer closeTimer = new System.Timers.Timer(200); 
     closeTimer.Elapsed += CloseTimerElapsed; 

     closeTimer.Start(); 
    } 

    private Dispatcher _currentDispatcher; 

    private void CloseTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 
     if (sender is System.Timers.Timer && sender != null) 
     { 
      (sender as System.Timers.Timer).Stop(); 
      (sender as System.Timers.Timer).Dispose(); 
     } 
     _currentDispatcher.BeginInvoke(new Action(() => Close())); 
    } 

    private void LoadTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 
     if (sender is System.Timers.Timer && sender != null) 
     { 
      (sender as System.Timers.Timer).Stop(); 
      (sender as System.Timers.Timer).Dispose(); 
     } 
     _currentDispatcher.BeginInvoke(new Action(() => LoadMainForm())); 
    } 
2

像我一样,你可能建立了这个事后的想法,并不想通过重新设计你的代码,以适应多线程架构的所有赫克...

首先创建一个名为SpashScreen新形式,在属性中点击BackgroundImage并导入你想要的任何图像。同时将FormBorderStyle设置为None,以便您不能单击x关闭屏幕。

public Form1() 
    { 
     InitializeComponent(); 

     BackgroundWorker bw = new BackgroundWorker(); 
     bw.WorkerSupportsCancellation = true; 
     bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
     bw.RunWorkerAsync(); // start up your spashscreen thread 
     startMainForm();  // do all your time consuming stuff here 
     bw.CancelAsync();  // close your splashscreen thread 
    } 


    private void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker worker = sender as BackgroundWorker; 

     SplashScreen ss = new SplashScreen(); 
     ss.Show(); 

     while (!worker.CancellationPending) //just hangout and wait 
     { 
      Thread.Sleep(1000); 
     } 

     if (worker.CancellationPending) 
     { 
      ss.Close(); 
      e.Cancel = true; 
     } 

    } 

这不支持进度条或任何花哨的东西,但我相信它可以调整。

+0

感谢您的示例。最后的'if'语句是不必要的 - 只要worker.CancellationPending为true,给定上面的while循环,代码将不会到达那里。 – 2015-09-16 22:27:17