5

I'm creating a Windows Form application which Dynamically creates controls based on data pulled from a Database.

I have the code working great in the background which loads the data from the database and applies it to variables, the problem I am having is when trying to create the controls using this data, I get a multi-threading error ( Additional information: Cross-thread operation not valid: Control 'flowpanelMenuRules' accessed from a thread other than the thread it was created on.)

I'm using the BackgroundWorker_DoWork event and the code that fails is the following:

Me.flowpanelMenuRules.Controls.Add(PanelRule(i))

The code before is a simple loop going through the variable (which is pulled from the database) and gathering the information.

Has anybody had any experience in safely invoking the above line? I just can't seem to get it to work at all :(

Thanks for the help, I can post more code if needed.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
Richard C
  • 401
  • 1
  • 5
  • 19
  • Please see https://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the?rq=1 for clarifications on what went wrong. – Dmitry Reznik Mar 27 '16 at 19:01
  • Controls shouldn't be created on a background thread. They get assigned to the thread that they're created in, and should only be used there. You should invoke the specific creation and updating of the controls, the rest can be done in the BGW. – Visual Vincent Mar 27 '16 at 19:02

2 Answers2

8

My recommendation is to have your BackgroundWorker simply create the controls, but not add them to the form. Instead, return the prepared controls to the calling/UI thread via the RunWorkerCompleted event. Then add the controls to your form at this point, possibly in conjunction with the SuspendLayout()/ResumeLayout() methods.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 1
    Joel - I used this idea and it works perfectly. I have the Background worker now running my SQL code to pull the information from the Database into the variables and then use the Background Worker Completed event to populate the results in the FlowLayoutPanel. Thank you! I have used Background Workers before but for some reason the Completed event just slipped my mind – Richard C Mar 27 '16 at 19:59
1

The native way for WinForms application is to use System.Winfows.Forms.Control methods like Invoke() and InvokeRequired property to accessing UI thread:

if(this.flowpanelMenuRules.InvokeRequired)
{
    this.flowpanelMenuRules.Invoke(() => AddPanelRule());
}
else
{
    AddPanelRule();
}

You can also use Dispatcher class.

If you are sure to be on a UI thread (e.g. in an button.Click handler), Dispatcher.CurrentDispatcher gives you the UI thread dispatcher that you can later use to dispatch from background threads to the UI thread as usual.

Dispatcher.CurrentDispatcher.Invoke(() => this.flowpanelMenuRules.Controls.Add(PanelRule(i)));
Vadim Martynov
  • 8,602
  • 5
  • 31
  • 43