2011-06-12 52 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); 
    } 
}