0

I'm trying to populate a grid from a list of anonymous type instances. I want to apply threading as this makes my application to freeze while populating the results. But I keep getting this this error:

list of anonymous type instances Control 'gridHotelDetails' accessed from a thread other than the thread it was created on. 

I've marked the line where the error is occuring. My Code:

public Thread MyThread { get; private set; }
public string search { get; set; }
public string searchCity { get; set; }
public string searchState { get; set; }

private void crawl()
{
    if (CheckForInternetConnection())
    {
        if (string.IsNullOrEmpty(search) || string.IsNullOrEmpty(search) || string.IsNullOrEmpty(searchCity) || string.IsNullOrWhiteSpace(searchCity) || string.IsNullOrEmpty(searchState) || string.IsNullOrWhiteSpace(searchState))
        {
            MessageBox.Show("Please fill up the search criterias");
        }
        else
        {
            string url = string.Format("http://www.yellowpages.com/{0}/hotels?g={1}&q={2}", searchCity, searchCity, search);
            if (!isURLExist(url))
            {
                MessageBox.Show("No records found. Please try again.");
            }
            else
            {
                Pages htmlDoc = new Pages(url);
                htmlDoc.pageResults();

                string searchUrl = string.Empty;
                for (int ii = 1; ii <= 1; ii++)
                {
                    searchUrl = url + "&page=" + ii;
                    htmlDoc.findHotels(searchUrl);

                    List<string> Name = htmlDoc.Names;
                    List<string> Address = htmlDoc.Address;
                    List<string> City = htmlDoc.City;
                    List<string> State = htmlDoc.State;
                    List<string> Zip = htmlDoc.Zip;
                    List<string> Phone = htmlDoc.Phone;
                    List<string> Website = htmlDoc.Website;

                    var CompleteInformation = Name.Zip(Address, (n, a) => new { Address = a, Name = n })
                        .Zip(City, (p, c) => new { p.Address, p.Name, City = c })
                        .Zip(State, (q, s) => new { q.Address, q.Name, q.City, State = s })
                        .Zip(Zip, (r, z) => new { r.Address, r.Name, r.City, r.State, Zip = z })
                        .Zip(Phone, (t, p) => new { t.Name, t.Address, t.City, t.State, t.Zip, Phone = p })
                        .Zip(Website, (u, w) => new { u.Name, u.Address, u.City, u.State, u.Zip, u.Phone, Website = w })
                        .ToList();

                    int rowCount = gridHotelDetails.Rows.Count;
                    foreach (var detail in CompleteInformation)
                    {
// Error occuring in this line 
                        gridHotelDetails.Rows.Add(rowCount++, detail.Name, detail.Address, detail.City, detail.State, detail.Zip, detail.Phone, detail.Website);  
                    }

                }
            }
            UseWaitCursor = false;
        }
    }
    else
    {
        MessageBox.Show("Internet Connection Not Available");
    }
}

private void btnSearch_Click(object sender, EventArgs e)
{
    search = txtSearch.Text.Trim();
    searchCity = txtCity.Text.Trim();
    searchState = ddState.Text.Trim();

    MyThread = new Thread(new ThreadStart(crawl));
    MyThread.Start();
    gridHotelDetails.Visible = true;
}

Why is this error occurring and how can I solve this error ?

Pranab
  • 382
  • 3
  • 10
  • What is your question? (P.S.: "criteria" is the plural of "criterion".) – O. R. Mapper Feb 27 '14 at 23:04
  • @O.R.Mapper : I've updated my question. Please have a look. – Pranab Feb 27 '14 at 23:11
  • Are you using WinForms or WPF? – Douglas Feb 27 '14 at 23:14
  • 2
    Asked many, many times. This seems to be the parent that everyone else gets marked as a duplicate to: http://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the – Erik Noren Feb 27 '14 at 23:15
  • @Douglas : Sir, I'm using WinForms – Pranab Feb 27 '14 at 23:15
  • @ErikNoren : Thank you sir for your reference. It helped me a lot in solving the error. Actually I'm very new to C# and stackoverflow thats why I'm not aware of accurate keyword to search for. I'll try not to repeat again. – Pranab Feb 27 '14 at 23:21
  • 1
    No worries! Welcome to SO. This is just the 3rd instance of this question I've seen today alone and I don't actually spend that much time here. :) – Erik Noren Feb 27 '14 at 23:22
  • @ErikNoren It's nice to have such humble guide like you in SO. Thank you once again and looking forward to have a beautiful coding time with guides like you. – Pranab Feb 27 '14 at 23:25

2 Answers2

1

You cannot make changes to a ui control from a thread other than the one it was created on. Please see this article for an example of how to accomplish what you want:

Trying to change UI control property from different thread using . InvokeRequired

Community
  • 1
  • 1
jtm001
  • 371
  • 2
  • 3
1

Short answer is you need to use Invoke on your UI elements when you update them from a non-UI thread. Button Clicks and handlers are usually not on the UI thread.

There should be a property on any UI element called InvokeRequired. If this is true, you need to use Invoke passing a delegate for the work you want done on that UI element. If it's false, you can just set the property directly (because you're on the UI thread).

So something like (though you'll want to take this check out of your foreach loop since it won't change during execution and you'll just waste cycles checking):

if (!gridHotelDetails.InvokeRequired)
{
    gridHotelDetails.Rows.Add(rowCount++, detail.Name, detail.Address, detail.City, detail.State, detail.Zip, detail.Phone, detail.Website);
}
else
{
    gridHotelDetails.Invoke(() =>
    {
        gridHotelDetails.Rows.Add(rowCount++, detail.Name, detail.Address, detail.City, detail.State, detail.Zip, detail.Phone, detail.Website);
    });
}
Erik Noren
  • 4,279
  • 1
  • 23
  • 29