我想将一个项目添加到表单上的列表框中,将其向下滚动到最后一个条目然后刷新它。我想在并行的ForEach循环中执行此操作
为此,我在网上找到了一种扩展方法,并根据我的需要进行了更改。现在我收到错误消息:“跨线程操作无效:控制'listBox1'从其创建的线程以外的线程访问。”。我理解错误是工作线程试图访问ListBox。实际上我可以看到主线程在收到错误之前可以更新ListBox。调试器也告诉我错误是在线“int visibleItems ...”
如何才能做到这一点?
public static class MyClass
{
public static void AddItemThreadSafe(this System.Windows.Forms.ListBox lb, object item)
{
int visibleItems = lb.ClientSize.Height / lb.ItemHeight;
if (lb.InvokeRequired)
{
lb.Invoke(new MethodInvoker(delegate
{
lb.Items.Add(item);
lb.TopIndex = Math.Max(lb.Items.Count - visibleItems + 1, 0);
lb.Refresh();
}));
}
else
{
lb.Items.Add(item);
lb.TopIndex = Math.Max(lb.Items.Count - visibleItems + 1, 0);
lb.Refresh();
}
}
}
答案 0 :(得分:-1)
您正在通过UI线程以外的线程访问ClientSize属性,这会导致行int visibleItems = lb.ClientSize.Height / lb.ItemHeight;
上的异常
如果你完全预先判断了visibleItems,你可以摆脱异常,但它仍然不是线程安全的代码:
public static void AddItemThreadSafe(this System.Windows.Forms.ListBox lb, object item)
{
if (lb.InvokeRequired)
{
lb.Invoke(new MethodInvoker(delegate
{
lb.Items.Add(item);
lb.TopIndex = Math.Max(lb.Items.Count - lb.ClientSize.Height / lb.ItemHeight + 1, 0);
lb.Refresh();
}));
}
else
{
lb.Items.Add(item);
lb.TopIndex = Math.Max(lb.Items.Count - lb.ClientSize.Height / lb.ItemHeight + 1, 0);
lb.Refresh();
}
}
你需要的是添加项目和刷新,一个原子操作(如果调度程序决定让它去另一个调用,则不允许执行暂停)。 您可以使用锁定:
private static readonly Object obj = new Object();
public static void AddItemThreadSafe(this System.Windows.Forms.ListBox lb, object item)
{
if (lb.InvokeRequired)
{
lb.Invoke(new MethodInvoker(delegate
{
lock (obj)
{
// thread unsafe code
lb.Items.Add(item);
lb.TopIndex = Math.Max(lb.Items.Count - lb.ClientSize.Height / lb.ItemHeight + 1, 0);
}
}));
}
else
{
lock (obj)
{
// thread unsafe code
lb.Items.Add(item);
lb.TopIndex = Math.Max(lb.Items.Count - lb.ClientSize.Height / lb.ItemHeight + 1, 0);
}
}
}
但是,如果你运行代码,使用Parallel.For,例如
Parallel.For(0, 1000, (x) =>
{
listBox1.AddItemThreadSafe(x);
});
表格会冻结。您需要确保UI可以呈现到目前为止的所有内容,因此您可以将其更改为:
Parallel.For(0, 1000, (x) =>
{
listBox1.AddItemThreadSafe(x);
Application.DoEvents();
});
它会正确呈现表单,就像这里: