In my application, I have a new Windows Form (called Monitor) which consists of basically a big display (which is a custom user control), which itself consists of many small charts (also user controls) arranged in a grid. When I instantiate the monitor form, a new display object is created, which then goes on to create a bunch of chart controls. The chart controls are then added to "display's" controls collection (Controls.Add(chart)
), which completes the loading of display, and then display is added to "monitor's" control collection, and the form is shown.
The problem I have is that each chart control takes about 0.5s to load, and we can have around 75 of them in one display object. Therefore, I would like to create these charts in parallel, to speed up the load time. I took advantage of the TPL method Parallel.ForEach
to accomplish this.
My code looks like this, inside the "display" object's constructor:
public class DisplayControl : UserControl
{
public DisplayControl()
{
var chartNames = new string[] { "chart1", "chart2", ... };
var builtCharts = new ConcurrentBag<Chart>();
// Create the chart objects in parallel
Parallel.ForEach(chartNames, name =>
{
// The chart constructor creates the chart itself, which is a
// custom user control object (inherits UserControl), composed
// of other various WinForm controls like labels, buttons, etc.
var chart = new Chart();
chart.Name = name;
builtCharts.Add(chart);
}
);
// Clean up the charts and add them to "display's" control collection
foreach(var chart in builtCharts)
{
// Do some unimportant modifications to the chart, synchronously
...
this.Controls.Add(chart);
}
}
}
The DisplayControl constructor is called on the main thread by the main form, Monitor, and the DisplayControl instance itself is added to Monitor's ControlCollection. Monitor is then shown using Monitor.Show()
, which is just the Show
method of a Form
class.
The main issue I am experiencing is that the DisplayControl constructor throws an InvalidOperationException
occasionally at the this.Controls.Add
line, citing the following:
System.InvalidOperationException: Cross-thread operation not valid: Control 'panel' accessed from a thread other than the thread it was created on.
I should note that panel
is just a panel WinForms control, which is created in the Chart()
constructor.
Finally, the code appears to work just fine about 90% of the time, and these errors are seemingly random. If I get the error, I can usually just re-run the code immediately and it will work.
My goal is to eliminate this error and make this all thread-safe.