多线程,访问UI控件

3
我有一个简单的应用程序,其中包含常规UI线程和后台工作器,在后台工作器中,我需要动态创建LinkLabels并将它们放置在FlowLayoutPanel中。为了做到这一点,我需要将LinkLabel的父级设置为FlowLayoutPanel。
以下是我目前拥有的代码片段,但是我在“l.Parent = panel;”这一行上得到了臭名昭著的“跨线程操作无效”。
我对多线程操作相当新,但我认为我正确地执行了调用,但显然没有。 有什么建议吗?
LinkLabel l = new LinkLabel();
if (rssFeedPanel.InvokeRequired) {
    FlowLayoutPanel panel = null;
    rssFeedPanel.Invoke(new MethodInvoker(delegate { panel = rssFeedPanel; }));
    l.Parent = panel;
}
else
    l.Parent = rssFeedPanel;
2个回答

3

您需要在其他线程上实际设置Parent属性。

LinkLabel l = new LinkLabel();
if (rssFeedPanel.InvokeRequired) {
    rssFeedPanel.Invoke(new MethodInvoker(delegate {
        l.Parent = rssFeedPanel;
    }));
}
else
    l.Parent = rssFeedPanel;

通常情况下,涉及访问UI控件成员的任何操作都只能从UI线程执行。一些明显的例外是InvokeInvokeRequiredBeginInvoke以及BackgroundWorker类的某些方法。

如果您希望在这种情况下,也可以使用BeginInvoke代替Invoke


只是好奇,你不能使用 if (Dispatcher.Thread == Thread.CurrentThread) 并调用 Dispatcher.BeginInvoke(new EventHandler<T>()) 来调用所有控件,而不是为每个控件检查 InvokeRequired 吗?只是一个脑抽,但如果可以的话,我相信它会快一点。(需要触发事件或其他什么东西,并通过 EventArgs 传递内容,但这很简单) - aevitas
Dispatcher.Thread 不是静态的,所以你需要 UI 线程的实例。至于速度更快,我表示怀疑。从代码上看,如果控制窗口已经被创建,我敢打赌它的速度大致相同。如果窗口尚未被创建,我敢打赌 Control.InvokeRequired 会更快。 - Dark Falcon

3
我建议您将逻辑放在一个方法中,并首先检查InvokeRequired,然后在Invoke内调用该方法,否则直接调用它。
if (rssFeedPanel.InvokeRequired) {
    rssFeedPanel.Invoke(new MethodInvoker(delegate 
    { 
        AddLabel();
    }));
}
else AddLabel();

把你的逻辑放在AddLabel方法中:

private void AddLabel()
{
    LinkLabel l = new LinkLabel();
    l.Parent = rssFeedPanel;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接