2010-07-13 32 views
3

我正在寫一個應用程序,它需要在加載數據庫時進行狀態欄更新。我將標籤設置爲「Database loading ...」,然後使用BackgroundWorker加載數據庫。當工作人員完成時,我將標籤設置爲「數據庫已加載」。但是,這隻能在啓動時完成,而且我不希望標籤可見很長時間,並且希望在工作人員完成後幾秒鐘後清除標籤。我可以在我的主設備上放置一個專用的計時器對象,但是這個操作只是它唯一的工作,而且似乎應該存在一個更優雅的解決方案。所以,我想lambda表達式:安排單火事件

void dataLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    DataLabel.Text = "Database Loaded"; 
    System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); 
    timer.Interval = 5000; 
    timer.Tick += new EventHandler((o, a) => 
    { 
     DataLabel.Text = ""; 
     (o as System.Windows.Forms.Timer).Enabled = false; 
    }); 
} 

當然timer的範圍函數退出後到期,這是Tick事件不會被調用。

如何在不使用全局實例定時器的情況下運行簡單的單火事件?

回答

2

你的代碼基本沒問題。事實上,在我看來,您的一般架構是最優雅的解決方案。你只是忘了啓動計時器。您不必擔心GC過早地收集定時器,因爲它在啓動時會自動「自動」自行「定向」。這當然會引起這樣的問題:由於每次創建一個新的定時器,這是否會導致內存泄漏。我認爲不會,因爲計時器在停止時也會「自行取消」。所以以下應該可以正常工作。

void dataLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    DataLabel.Text = "Database Loaded"; 
    var timer = new System.Windows.Forms.Timer(); 
    timer.Interval = 5000; 
    timer.Tick += (o, a) => 
    { 
     timer.Stop(); 
     DataLabel.Text = ""; 
    }; 
    timer.Start(); 
} 
+0

我不能相信我忘了開始計時器......我非常渴望責怪GC!不幸的是,它在GC停止時並沒有得到它,所以我只是將'Stop'切換爲'Dispose'。 (我把一個處理程序掛到了'Disposed'事件上,它從來沒有被調用過。)另外,在'})行中有一個流浪的圓括號:' – dlras2 2010-07-14 13:22:12

+1

@Daniel:我修正了錯字。關於GC ...我說定時器根源本身。我在Reflector中查看它,它在做的方式是在定時器啓動時調用'GCHandle.Alloc',然後在停止時釋放該句柄。調用'Dispose'或'Stop'與我所能說的完全相同。所以停止定時器應該足夠了。我認爲'Disposed'事件只會在調用Dispose時引發,而不會在GC運行終結器時引發。 – 2010-07-14 13:56:08

0

通常,即使在Visual Studio中,狀態仍然存在,直到用戶執行其他操作。這似乎是一個更好的方法,因爲用戶可能無法在5秒或10秒內檢查狀態。我認爲它更好地更新狀態時使用做一些其他行動(如點擊菜單等)

1

你可以創建一個類變量Timer,使用它一次,然後null它。

此外,您可以使用ThreadPool註冊一個方法 - 在此線程中,在觸發UI更新標籤前調用所需的數量。

或者你可以重新使用後臺工作人員獲得相同的效果。這可以節省您需要Control.Invoke到UI線程上...

2

也許您可以在其正文中使用Thread.Sleep(N)的某種方法的異步調用。

+0

+1我的想法正好笑 – 2010-07-13 14:22:16

0

你很可能只是讓後臺線程等待的時間長一點,如:

void dataLoader_DoWork(object sender, DoWorkEventArgs e) { 
    // Do work normally. 

    // Report progress as complete. 
    var worker = sender as BackgroundWorker; 
    worker.ReportProgress(100); 

    Thread.Sleep(5000); 
} 

void dataLoader_ProgressChanged(object sender, ProgressChangedEventArgs e) { 
    if (e.ProgressPercentage == 100) { 
    // Set label here 
    DataLabel.Text = "Database Loaded"; 
    } 
} 

void dataLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    DataLabel.Text = ""; 
} 

這可能工作比射擊異步方法,因爲線程已經爲背景工創造更好一點無論如何。這會更好嗎?

+0

我相當肯定BackgroundWorker的使用線程池反正。 – 2010-07-13 14:42:10

0

隊列中的工作項目中的線程池,睡眠持續5秒鐘,然後BeginInvoke更新行爲。這裏有一個簡單的例子:

using System; 
using System.Threading; 
using System.Windows.Forms; 

namespace CSharpScratch 
{ 
    class Program 
    { 
     private static void Main() 
     { 
      var myForm = new MyForm(); 
      myForm.ShowDialog(); 
     } 
    } 

    class MyForm : Form 
    { 
     private readonly Label _label; 

     public MyForm() 
     { 
      _label = new Label {Text = "Hello", Parent = this}; 
      Load += FormLoaded; 
     } 

     public void FormLoaded(object sender, EventArgs args) 
     { 
      ThreadPool.QueueUserWorkItem(x => 
       { 
        Thread.Sleep(5000); 
        BeginInvoke(new Action(() => _label.Text = "Goodbye")); 
       }); 
     } 
    } 
}