我需要一些帮助,试图弄清楚我做错了什么。我正在尝试从单独的线程中获取系统日志中的项集合,以防止表单在收集过程中被冻结。我可以让后台工作者全部抓住它们,但我有一些问题将它们添加到表单上的ListBox
。
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);
}
}
显然这没有按预期工作,因为有两个独立的线程,你不能像我发现的那样更改不同线程上的对象。所以,如果有人能指导我朝着正确的方向前进,我会很感激。
答案 0 :(得分: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
,因为它的重点是使用与UI线程同步的ProgressChanged
和RunWorkerCompleted
事件处理程序。
答案 1 :(得分:1)
您需要使用后台工作程序的报告进度选项。 google this
答案 2 :(得分: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;
}
答案 3 :(得分:1)
尝试这个,在Control的线程上调用动作的非常简单的方法:
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);
}
}
答案 4 :(得分:0)
只允许GUI线程修改GUI元素。如果您不遵守此规则,您将获得例外。你可以:
MethodInvoker
从GUI线程中触发调用