2011-06-12 60 views
5

我需要一些幫助,試圖找出我做錯了什麼。我試圖從一個單獨的線程的系統日誌中獲取項目的集合,以防止表單在收集過程中被凍結。我可以讓後臺工作人員抓住他們,但我有一些問題將他們添加到窗體上的ListBox使用線程C#

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 

    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries) 
    { 
    listBox1.Items.Add(
     entry.EntryType.ToString() + " - " + 
     entry.TimeWritten + "  - " + 
     entry.Source); 
    } 
} 

顯然預期,這並不工作,因爲有2個獨立的線程,你不能在不同的線程改變的對象,因爲我已經找到了。所以,如果有人能指引我朝着正確的方向發展,我會很感激。

+1

就以這個答案一看:http://stackoverflow.com/questions/1136399/how-to -update-文本框上的圖形用戶界面,從-另一個線程在C# – Klinger 2011-06-12 07:25:15

回答

4

您不應該從非UI線程訪問UI元素。運行ReportProgress,它將與UI線程同步。

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries) 
    { 
     var newEntry = entry.EntryType + " - " + entry.TimeWritten + "  - " + entry.Source; 
     backgroundWorker1.ReportProgress(0, newEntry); 
    } 
} 

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    var newEntry = (string)e.UserState; 
    listBox1.Items.Add(newEntry); 
} 

確保啓用WorkerReportsProgress

backgroundWorker1.WorkerReportsProgress = true; 

和訂閱ProgressChanged

backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged; 

另一種方法是調用Control.Invoke

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries) 
    { 
     var newEntry = entry.EntryType.ToString() + " - " + entry.TimeWritten + "  - " + entry.Source; 
     Action action =() => listBox1.Items.Add(newEntry); 
     Invoke(action); 
    } 
} 

但是這種方法不需要BackgroundWorker爲整點是用ProgressChangedRunWorkerCompleted事件處理程序,它們與UI線程同步。

1

您需要使用後臺工作人員的報告進度選項。 google this

0

只允許GUI線程修改GUI元素。如果你不尊重這條規則,你會得到一個例外。您可以:

  1. 使用MethodInvoker從GUI線程
  2. 內觸發呼叫使用報告進度的功能(實際上1.爲你做)
  3. 存儲在另一個數據結構中的對象(鎖)並在GUI線程中使用計時器來拉取對象並顯示它們。
1

不應該有任何問題,因爲您正在使用BackgroundWorker。所有對回調方法的調用都在相同的UI上下文中運行。

編輯:

如果你想報告進度,您需要存儲SynchronizationContext.Current到最好的啓動。或者你可以使用IsInvokeRequired模式。這裏是我如何使用SynchronizationContext

private SynchronizationContext uiContext; 
     public Form1() 
     { 
      uiContext = SynchronizationContext.Current; 
      InitializeComponent(); 
      FillItem(); 
     } 

我有下面的代碼,它像魅力一樣工作。

public void FillItem() 
      { 
       BackgroundWorker worker = new BackgroundWorker(); 
       worker.WorkerReportsProgress = true; 
       worker.DoWork += (a, b) => 
            { 
             int i = 0; //Percentage complete, roll your own logic. 
             foreach (var eventLog in EventLog.GetEventLogs()) 
             { 
              foreach (EventLogEntry entry in eventLog.Entries) 
              { 
               this.listBox1.Items.Add(entry.Message); 
uiContext.Post(z=>worker.ReportProgress(i++),null); 

              } 
             } 


            }; 
       worker.RunWorkerAsync(); 
       worker.ProgressChanged += (a, b) => this.progressBar1.Value = b.ProgressPercentage; 


      } 
1

試試這個,調用控件的線程上的動作非常簡單的方法:

private void Form1_Load(object sender, EventArgs e) 
{ 
    var bw = new BackgroundWorker(); 
    bw.DoWork += DoWork; 
    bw.RunWorkerAsync(); 
} 
private void DoWork(object sender, DoWorkEventArgs e) 
{ 
    var itemList = new List<int> {1, 22, 3, 4}; 
    var func = new Action<int>(itemToAdd => listBox1.Items.Add(itemToAdd)); 
    foreach (var item in itemList) 
    { 
     listBox1.Invoke(func, item); 
    } 
}