1

I think code talks clear

private void ucPerson_Load(object sender, EventArgs e)
{
    person = new Person();
    BackgroundWorker backgroundBinder = new BackgroundWorker();
    backgroundBinder.DoWork += BindComboBoxes;
    backgroundBinder.RunWorkerAsync();
}

private void BindComboBoxes(object sender, DoWorkEventArgs e)
{
    cmbEducationLevel.DataSource = Program.eService.GetEducationLevels();
    cmbNationality.DisplayMember = "Name";
    cmbNationality.ValueMember = "NationalityID";
}

Error I get:

Cross-thread operation not valid: Control 'cmbNationality' accessed from a thread other than the thread it was created on.

What need I do to make it possible for my background-worker's thread to access the combobox?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Mahdi Tahsildari
  • 13,065
  • 14
  • 55
  • 94
  • 2
    You have to post-pone the assigning of the `DataSource` to after the worker finished running. – Uwe Keim Feb 06 '13 at 12:58
  • @UweKeim how can I find out when the worker has finished running? can you show me an example? – Mahdi Tahsildari Feb 07 '13 at 05:58
  • The `RunWorkerCompleted` is fired to your foreground thread. So doing the work in this handler, just as in the answer you accepted, is a good way. – Uwe Keim Feb 07 '13 at 06:09

3 Answers3

3

The backgroundworker works in another thread : you are not allowed to call controls that belong to the calling thread in it.

As said Uwe Keim, you have to put everything that touches the controls in the RunWorkerCompleted event :

private void ucPerson_Load(object sender, EventArgs e)
{
    person = new Person();
    BackgroundWorker backgroundBinder = new BackgroundWorker();
    backgroundBinder.DoWork += GetData;
    backgroundBinder.RunWorkerCompleted += BindComboBoxes;
    backgroundBinder.RunWorkerAsync();
}

<<yourReturnType>> source;

private void GetData(object sender, DoWorkEventArgs e)
{
    source = Program.eService.GetEducationLevels();
}

private void BindComboBoxes(object sender, RunWorkerCompletedEventArgs e)
{
    cmbNationality.DisplayMember = "Name";
    cmbNationality.ValueMember = "NationalityID";
    cmbNationalty.DataSource = source;
}
Larry
  • 17,605
  • 9
  • 77
  • 106
0

The best way would be the example of Laurent

Dirty way:

cmbEductionLevel.Invoke((MethodInvoker)delegate { mbEducationLevel.DataSource = Program.eService.GetEducationLevels(); });

cmbNationality.Invoke((MethodInvoker)delegate { 
     cmbNationality.DisplayMember = "Name";
     cmbNationality.ValueMember = "NationalityID"; 
});
Random IT Guy
  • 625
  • 1
  • 8
  • 16
-1

You should use Invoke and pass a delegate in order to update the control.

Radin Gospodinov
  • 2,313
  • 13
  • 14
  • 1
    Invoking from a background thread, especially a background worker, seems like a bad design decision to me. – Uwe Keim Feb 06 '13 at 13:04