1

I'm trying to load data to a combobox and it's working when i run the form without debugging. But when i try to run it with debug mode I get an error :System.InvalidOperationException: 'Cross-thread operation not valid: Control 'ResultBox' accessed from a thread other than the thread it was created on.' How can I fix this? And btw a have a background worker that writes the message when the data is downloaded. I read somewhere that this issue can be fixed using backgroundworker. Here is the form code:

public Form1()
    {
        InitializeComponent();
        backgroundWorker1.WorkerReportsProgress = true;
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }

    private Task GetDataTask() {
        return Task.Factory.StartNew(() => {
            List<Result> results = new List<Result>();
            results = 
 GetResults.GetData("http://worldcup.sfg.io/teams/results/");
            foreach (var result in results)
            {
                ResultBox.Items.Add(result.Fifa_Code);
            }
        });
    }

    private void ResultBox_SelectedIndexChanged(object sender, EventArgs e)
    {

    }

    private void SaveTeam_Click(object sender, EventArgs e)
    {

    }

    private async void backgroundWorker1_DoWork(object sender, 
 DoWorkEventArgs e)
    {
        await GetDataTask();
    }

    private void backgroundWorker1_ProgressChanged(object sender, 
 ProgressChangedEventArgs e)
    {
        resultsLoadedLabel.Text = (e.ProgressPercentage.ToString() + "%");
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, 
 RunWorkerCompletedEventArgs e)
    {
        resultsLoadedLabel.Text = "Podaci učitani!";
    }

This line of code inside foreach gets the exception :

ResultBox.Items.Add(result.Fifa_Code);

This is the class that gets results:

public class GetResults
{

    public static List<Result> GetData(string url) {
        var client = new RestClient();
        var request = new RestRequest(url, Method.GET);
        request.AddHeader("User-Agent", "Nothing");
        IRestResponse<List<Result>> results = client.Execute<List<Result>> 
   (request);
        return results.Data;
    }
}

1 Answers1

2

If going async, then go async all the way. RestSharp allows you to make async calls

So you can refactor data access to

public class GetResults {

    public static async Task<List<Result>> GetDataAsync(string url) {
        var client = new RestClient();
        var request = new RestRequest(url, Method.GET);
        request.AddHeader("User-Agent", "Nothing");
        IRestResponse<List<Result>> results = await client.ExecuteTaskAsync<List<Result>>(request);
        return results.Data;
    }
}

Next, since the form load is an event handler, you can make that async as well and load the data.

public Form1() {
    InitializeComponent();
}

private async void Form1_Load(object sender, EventArgs e) {
    //On UI thread
    resultsLoadedLabel.Text = "Loading data!";
    //get data on separate thread (non blocking)
    List<Result> results = await GetResults.GetDataAsync("http://worldcup.sfg.io/teams/results/");
    //Back on UI thread
    resultsLoadedLabel.Text = "Podaci učitani!";
    foreach (var result in results) {
        ResultBox.Items.Add(result.Fifa_Code);
    }
}

Making sure when accessing UI controls while using async calls that they are accessed on the main thread that created them (see comments).

Nkosi
  • 235,767
  • 35
  • 427
  • 472