views:

523

answers:

3

OK, please disregard what has gone before. I'm not getting the errors anymore, so it seems my problem is with getting a Chart to update when I change the values to which the Chart is data bound.

//Disregard below here

Hi all. I have a WinForms application that has a panel, panel1. A background thread creates some other controls that then get added to panel1 like so

panel1.Controls.AddRange(myArrayOfControls);

This works great and I can see my controls get added. But, when new data comes in on another thread, I update values in the controls' parent objects and then need to Refresh() to get the display to update with the new values. Calling Refresh() in either context, the thread where the data comes in or the objects that receive the updated data causes an InvalidOperation exception because Invoke is required. I've tried using Invoke in my model objects and also the thread where the data is incoming and can't seem to shake the error.

If anyone has some guidance I'd greatly appreciate it.

UPDATE: Here's a little more info. I didn't think it would require it, but I was wrong. :)

I have an object class MyObject. This MyObject class gets created in a thread called topologyThread. Data comes in on dataThread. Instances of MyObject have a Panel instance variable and the Panel has child Controls including two Charts from the System.Windows.Forms.DataVisualization.Charting namespace. So, as data comes in on dataThread, I update the respective data values in the MyObject objects and then need to refresh the Charts to show the updated data.

I do know the data is processing fine. In my MyObject class, I'm logging the new values to Console in the setter for the property and see the new values show up.

+5  A: 

You must do both operations (refresh and updating of control's parent object) from the main UI thread. If you are modifying a control from a background thread and not getting an exception that is bad luck because it is definitely an error.

The best way to do this is to use

theControl.Invoke(new MethodInvoker(MyUpdateMethod));

If you have a sample of how the update is done, we can give a better sample on how to properly call it from the background thread.

JaredPar
A: 

JaredPar's answer is good. It can cause problems in some instances (notably when the method is invoked before the form is finished being constructed). Here's a somewhat more robust implementation (using extension methods)

public static class ControlInvokeExtensions
{
    public static void InvokeOnHostThread(Control host, MethodInvoker method)
    {
        if (IsHandleCreated)
            Invoke(new EventHandler(delegate { method(); }));
        else
            method();
    }
}

now you can call it this way

panel1.InvokeOnHostThread(() => panel1.Controls.AddRange(myArrayOfControls));

or if you're in the form:

InvokeOnHostThread(() => panel1.Controls.AddRange(myArrayOfControls));
Michael Meadows
+1  A: 

JaredPar is a pretty good answer. I would like to add to it a bit as to the reason your code sort of works.

With windows forms you can talk to the UI thread from other threads. This is really bad practice in all cases.

The catch is that when you do it, it is hard to catch because sometimes the UI will work as if nothing is wrong. item will get added or changed and the UI will reflect the changes. However other times running the same exact code, it will not work.

That is the catch with touching the UI from any thread other then the UI thread. The out come is inconsistent and that is why is very bad practice.

God I wish I could comment. :)

Tony
+1, now you're 10 rep closer to being able to comment. :)
Michael Meadows