3

I have a simple app with the usual UI thread and a background worker, in the background worker I need to dynamically create LinkLabels and place them in a FlowLayoutPanel. In order to do this I need to set the parent of the LinkLabel to the FlowLayoutPanel.

Here is a snippet of code I currently have, however, I get the infamous "Cross-thread operation not valid" on the line 'l.Parent = panel;'

I'm fairly new to multithreaded operations but I thought I did the invoking correctly, but obviously not. Any Suggestions?

LinkLabel l = new LinkLabel();
if (rssFeedPanel.InvokeRequired) {
    FlowLayoutPanel panel = null;
    rssFeedPanel.Invoke(new MethodInvoker(delegate { panel = rssFeedPanel; }));
    l.Parent = panel;
}
else
    l.Parent = rssFeedPanel;
Hammy
  • 109
  • 1
  • 9

2 Answers2

3

You need to actually set the Parent property on the other thread.

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

In general, nearly any operation involving accessing the members of a UI control can only be done from the UI thread. Some obvious exceptions are Invoke, InvokeRequired, BeginInvoke, and some of the methods of the BackgroundWorker class.

If you wish for this case, you can also use BeginInvoke instead of Invoke.

Dark Falcon
  • 43,592
  • 5
  • 83
  • 98
  • Just curious, can't you use `if (Dispatcher.Thread == Thread.CurrentThread)` and call `Dispatcher.BeginInvoke(new EventHandler())` to invoke all controls as opposed to checking whether `InvokeRequired` for each control? Just a brain fart, but if you can, I'm pretty sure it's a bit faster. (Does require you to fire an event or something and passing the stuff through `EventArgs`, but that's quite simple) – aevitas Jan 08 '12 at 00:54
  • `Dispatcher.Thread` is non-static, so you'd need the instance for the UI thread. As for being faster, I doubt it. From looking at the code I'd bet it is about the same if the control window as been created. If the window has not been created, I'd bet `Control.InvokeRequired` is faster. – Dark Falcon Jan 08 '12 at 01:19
3

I recommend you to put your logic in one method and first check if InvokeRequired then call that method within Invoke otherwise call it directly.

if (rssFeedPanel.InvokeRequired) {
    rssFeedPanel.Invoke(new MethodInvoker(delegate 
    { 
        AddLabel();
    }));
}
else AddLabel();

And put your logic in AddLabel method:

private void AddLabel()
{
    LinkLabel l = new LinkLabel();
    l.Parent = rssFeedPanel;
}
Qorbani
  • 5,825
  • 2
  • 39
  • 47