2010-08-13 149 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