Razim Khan
Razim Khan

Reputation: 347

Cross thread operation not valid in BackgroundWorker

I want to display some data on form load in a data gridview , the data which i want to display is in large number of rows , when i use background worker processor it show me the following error.

My code:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        FTPUtility obj = new FTPUtility();
        dataGridViewRequest.DataSource = obj.ListRequestFiles();
        dataGridViewRequest.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
        dataGridViewRequest.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
        dataGridViewRequest.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;

        dataGridViewResponses.DataSource = obj.ListResponseFiles();
        dataGridViewResponses.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
        dataGridViewResponses.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
        dataGridViewResponses.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;

    }
    catch (Exception ex)
    {

        MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

Form load:

private void FormFTP_Load(object sender, EventArgs e)
{
    try
    {

        //this.comboBoxRequests.SelectedIndex = 0;
        backgroundWorker1.RunWorkerAsync();

    }
    catch (Exception ex)
    {

        MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

Upvotes: 0

Views: 1787

Answers (2)

Reza Aghaei
Reza Aghaei

Reputation: 125197

There are many different ways to prevent the form from being freezed.
For example you can load your data like this:

private async void Form_Load(object sender, EventArgs e)
{
    //do some initializations
    await LoadData();
    //do some other initializations that you need to perform.
}

private async Task LoadData()
{
    //Load your data here
    //For example
    FTPUtility obj = new FTPUtility();
    dataGridViewRequest.DataSource = obj.ListRequestFiles();
}

This way when running the form, the commands run in the sequence you wrote while the UI is responsive and you will not face with common difficulties of using BackgroundWorker or threads like Cross thread operation exceptions.

The key point is in using async/await. For more information read Asynchronous Programming with Async and Await

Remember that this way, every where you want to call LoadData, you should call it this way:

await LoadData();

And the method that you write this code in, should be async:

private async void RefreshButton_Click(object sender, EventArgs e)
{
    await LoadData();
}

EDIT For .Net 4.0


For .Net 4.0 you can use both Task.Run or BackgroundWorker. I recommend Task.Run because it is more simple and more readable.

Please note Cross Thread Operation Exception will throw when you access the UI elements from another thread than UI. In this situations you should use this.Invoke(new Action(()=>{/*Access UI Here*/})); instead. And never put a time-consuming task in your invoke part.

BackgroundWorker Approach:

private void Form1_Load(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
    //If you put some code here for example MessageBox.Show(""); 
    //The code will immadiately run and don't wait for worker to complete the task.
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    this.LoadData();
}

public void LoadData()
{
    var ftp = new FtpUtility();
    var data = ftp.ListRequestFiles();
    this.Invoke(new Action(() =>
    {
        //Setup columns and other UI stuff
        //Set datasource of grid
        this.dataGridView1.DataSource = data;
    }));
}
  • Remember everywhere you formerly used LoadData, now you should use backgroundWorker1.RunWorkerAsync(); instead.
  • If you want to do a job after the worker completes the task, put your job in backgroundWorker1_RunWorkerCompleted or in Invoke part of LoadData.

Task.Run Approach

private void Form1_Load(object sender, EventArgs e)
{
    Task.Run(() =>
    {
        LoadData();
    })
    .ContinueWith(x =>
    {
        //You can put codes you want run after LoadData completed here
        //If you access the UI here, you should use Invoke
    });
    //If you put some code here for example MessageBox.Show(""); 
    //The code will immadiately run and don't wait for worker to complete the task.
}

public void LoadData()
{
    var ftp = new FtpUtility();
    var data = ftp.ListRequestFiles();
    this.Invoke(new Action(() =>
    {
        //Setup columns and other UI stuff
        //Set datasource of grid
        this.dataGridView1.DataSource = data;
    }));
}
  • Remember everywhere you formerly used LoadData, now you should use Task.Run(()=>{LoadData();}); instead.
  • If you want to do a job after the LoadData completes, put your job in ContinueWith or in Invoke part of LoadData.

Upvotes: 4

dan
dan

Reputation: 532

Try this.

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {

       dataGridViewRequest.Invoke(new Action(() => {
            FTPUtility obj = new FTPUtility();
            dataGridViewRequest.DataSource = obj.ListRequestFiles();
            dataGridViewRequest.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridViewRequest.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridViewRequest.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;

            dataGridViewResponses.DataSource = obj.ListResponseFiles();
            dataGridViewResponses.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridViewResponses.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            dataGridViewResponses.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
       }));
    }
    catch (Exception ex)
    {

        MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

private void FormFTP_Load(object sender, EventArgs e)
{
    try
    {

        //this.comboBoxRequests.SelectedIndex = 0;
        backgroundWorker1.RunWorkerAsync();

    }
    catch (Exception ex)
    {

        MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

Upvotes: 1

Related Questions