I am trying to display some information on a grid queried from a sql server. The data gathering can take about 10 seconds so I don’t want to lock the UI thread.
I currently have code like:
ThreadPool.QueueUserWorkItem(DataUpdateThread, new UpdateParams(year));
private struct UpdateParams
{
internal string year;
internal UpdateParams(string year)
{
this.year = year;
}
}
private void DataUpdateThread(object state)
{
DataTable dTable = new DataTable();
try
{
this.Invoke((MethodInvoker)delegate
{
//stop data editing on the grid
//scrolling marquee for user
marquee.Visible = true;
marquee.Enabled = true;
grdMain.Visible = false;
grdMain.DataSource = null;
});
UpdateParams parameters = (UpdateParams)state;
dTable = GetData(parameters.year);
}
catch (Exception ex)
{
this.Invoke((MethodInvoker)delegate
{
//log error + end user message
});
}
finally
{
this.Invoke((MethodInvoker)delegate
{
grdMain.DataSource = dTable;
grdMainLevel1.RefreshData();
marquee.Visible = false;
marquee.Enabled = false;
grdMain.Visible = true;
});
}
}
This works most of the time apart from if the form it is on is closed before the update completes it will crash with the error:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
I understand the error will be because the form no longer exists so when the finally section tries to invoke the method on the UI thread it can’t.
Is there a better way to do this whole thing? I guess I can handle the invoke errors but it looks messy and I think I have probably missed a simpler way.
You can check whether the form has been closed and don’t do the invoke if the form has been closed.
if (this.IsHandleCreated)should work. This can however still give problems because the form can be closed between the check and the call toBeginInvoke. The only ‘full-proof’ solution is then to enclose the entire call in a try/catch.