codewarrior
codewarrior

Reputation: 193

Cross thread communication while accessing datagrid

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

Answers (2)

amarnath chatterjee
amarnath chatterjee

Reputation: 1962

Using datagrid in multithreaded environment is tricky. Ideally you should :

  1. Create a background thread to process data and raise events to update datagrid.
  2. Update UI controls in UI thread only.
  3. There can be more than one UI control which will work together to achieve the overall functionality (Progress bar, buttons, etc.). Do not pass on control reference in different thread.
  4. The source of data to which the datagrid will be bound should not be modifiable by any other class.

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

Shawn Kovac
Shawn Kovac

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

Related Questions