views:

99

answers:

1

I have a function which adds a control to a parent control which is called from threads different to the thread the controls were created on. This is how it goes:

1        delegate void AddControlToParentDelegate(Control child, Control parent);
2        private void addControlToParent(Control child, Control parent) {
3        if (parent.InvokeRequired) {
4            AddControlToParentDelegate d = new  AddControlToParentDelegate(addControlToParent);
5            this.Invoke(d, new object[] { child, parent });
6            } else {
7                parent.Controls.Add(child);
8            }
9        }
10    }

This works fine up until both parent.InvokeRequired as well as child.InvokeRequired are TRUE. Then, once line 5 is executed (now the delegate d is invoked and the function is supposed to be running on the UI thread. (right?)) child on line 7 throws a cross-thread operation invalid exception. Why is this? Isn't it already running on the thread it was created?

I managed to fix this by adding an extra (child.InvokeRequired) check:

delegate void AddControlToParentDelegate(Control child, Control parent);
private void addControlToParent(Control child, Control parent) {
    if (parent.InvokeRequired) {
        AddControlToParentDelegate d = new AddControlToParentDelegate(addControlToParent);
        this.Invoke(d, new object[] { child, parent });
    } else {
        if (child.InvokeRequired) {
            this.Invoke(new MethodInvoker(delegate() {
            parent.Controls.Add(child);
            }));
        } else {
            parent.Controls.Add(child);
        }
    }
}

but this just seems terribly hack-y and unnecessary. Is this the way to do it? Or have I totally missed the bus?

+5  A: 

Note that InvokeRequired is unreliable when the control has no window handle. This will almost certainly be the case for a newly created child control that does not have a parent yet, which appears to be the case in your "fixed" code.

See Ivan Krivyakov's detailed analysis: http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html

dthorpe
In addition, designs that do *not* use `InvokeRequired` are more maintainable. I recommend scheduling a `Task` to the ui thread or using `BackgroundWorker.ReportProgress`, both of which are platform-independent solutions (i.e., not tied to Windows Forms).
Stephen Cleary