Reputation: 193
I am trying to diaplay datagridview and then populate the rows dynamically. If I populate on the UI thread, the datagrid freezes till all data is loaded and then it displays the datagrid. So, I created another thread where Iam adding data to the datagrid. But it is showing me error. Cross thread communication.
My code is as follows.
private void btnStartTest_Click(object sender, EventArgs e)
{
tbCtrl.Visible = true;
viewData.Show();
viewData.ScrollBars = ScrollBars.None;
//tbCtrl.Size = MaximumSize;
Thread thr = new Thread(LoadData);
thr.Start();
}
public void LoadData()
{
for (int i = 0; i < 20; i++)
{
int firstDisplayed = viewData.FirstDisplayedScrollingRowIndex;
int displayed = viewData.DisplayedRowCount(true);
int lastVisible = (firstDisplayed + displayed) - 1;
int lastIndex = viewData.RowCount - 1;
viewData.Rows.Add();
if (lastVisible == lastIndex)
{
viewData.FirstDisplayedScrollingRowIndex = firstDisplayed + 1;
}
viewData.Rows[i].Cells[0].Value = System.DateTime.Now.Hour + ":" +
for (int j = 1; j < 7; j++)
{
viewData.Rows[i].Cells[j].Value = (i + j).ToString();
}
Thread.Sleep(1000);
}
}
Kindly tell me how can I achieve this without UI hanging. I am not pulling data from database. There are no calculations to be done.
Upvotes: 2
Views: 285
Reputation: 1962
Using datagrid in multithreaded environment is tricky. Ideally you should :
I have posted a template here, covering these issues... works fine for me... http://www.codeproject.com/Articles/1086714/A-Template-For-Using-Datagrid-in-Monitoring-UI
Upvotes: 0
Reputation: 1435
you can access the viewData inside the worker thread, but not without possible (and uncertain) problems. in short: it is not safe to do so. easiest way that i know is to create a new public static class such as this (the name of the class is unimportant and really can be anything you want because it is only used for extension methods), and then put these two methods in that class as below:
static partial class MyExtensions
{
public static TResult SafeInvoke<T, TResult>(this T isi, Func<T, TResult> callFunction) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired)
{
IAsyncResult result = isi.BeginInvoke(callFunction, new object[] { isi });
object endResult = isi.EndInvoke(result); return (TResult)endResult;
}
else
return callFunction(isi);
}
/// <summary>
/// This can be used in C# with:
/// txtMyTextBox.SafeInvoke(d => d.Text = "This is my new Text value.");
/// or:
/// txtMyTextBox.SafeInvoke(d => d.Text = myTextStringVariable);
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="isi"></param>
/// <param name="callFunction"></param>
public static void SafeInvoke<T>(this T isi, Action<T> callFunction) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) isi.BeginInvoke(callFunction, new object[] { isi });
else
callFunction(isi);
}
} // static class MyExtensions
then every time you access your viewData
variable inside your LoadData() method, you need to use these methods to access it instead. these methods call the methods in a 'safe' way. so viewData.Rows.Add();
would be replaced with viewData.SafeInvoke(d => d.Rows.Add());
.
to understand what is going on, you can read about extension methods and lambda expressions. but this is the cleanest way i've seen how to do it. and i've seen a LOT of other ways, but most are more than twice as complicated. i copied this code years ago as this is not an uncommon problem. happy coding! :)
Upvotes: 2