0

I'm populating datagridview with random numbers from a given range, but when I generate a big amount of numbers - my program hangs while generating them. That could last more that a minute (depending on the amount). I know that I can show a progress using ProgressBar. I've tried to use it, but I haven't got anythin. Any examples of using it?

Here is my code:

private void button1_Click(object sender, EventArgs e)
{
    if (dataGridView1.RowCount > 0) {
        dataGridView1.Rows.Clear();
        dataGridView1.Refresh();
    }

    N = int.Parse(textBox1.Text);
    range_min = int.Parse(textBox2.Text);
    range_max = int.Parse(textBox3.Text);
    numbers = new int[N];
    if (range_max < range_min) MessageBox.Show("Some alert text");
    else if (range_max == range_min) MessageBox.Show("Some alert text");
    else
    {
        dataGridView1.RowCount = N;
        for (int i = 0; i < N; i++)
        {
            numbers[i] = (int)(Math.Round((range_max - range_min) * rndm.NextDouble() + range_min));
            dataGridView1[0, i].Value = numbers[i];
        }
    }
}
Oleg
  • 35
  • 4
  • 1
    Does this answer your question? [Progressbar for loading data to DataGridView using DataTable](https://stackoverflow.com/questions/18377818/progressbar-for-loading-data-to-datagridview-using-datatable) –  May 08 '21 at 17:23
  • Just replace the loading from datatable of the suggested duplicate by your logic. You can also improve that by setting the maximum with the number of rows, if known, and call DoProgress on each iteration, or you can use what I call a "quantum stepping", every 10 or 100 or 1000... rows you do the progress by 1 (thus the maximum is count / quantum)... this avoid to stress the UI and to break the performance of the loop when having large amount of lines. You can do that threaded (main UI thread sync needed) or not, and use a Refresh after each progress if needed. –  May 08 '21 at 17:30
  • Ok @OlivierRogier , I've just tried this, but I am getting System.InvalidOperationException: "Invalid operation on multiple threads: An attempt was made to access control 'dataGridView1' from a different thread from which it was created." How can I solve it? Also, do I need to copy setDataSource() method into my code, or not? – Oleg May 08 '21 at 18:23
  • See for example [How do I update the GUI from another thread?](https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread) and [How to access a WinForms control from another thread i.e. synchronize with the GUI thread?](https://stackoverflow.com/questions/58657831/how-to-access-a-winforms-control-from-another-thread-i-e-synchronize-with-the-g/58658119#58658119) –  May 08 '21 at 18:27
  • @OlivierRogier, could you, please, explain it to me and write me an example? I'm new in C# at all. – Oleg May 08 '21 at 18:36
  • UI controls make *terrible* variable and collection holders. Populate a list and bind it as the datasource. The problem is the time to access the control and time for Windows to repaint after each row. Databinding means one painting and no need to distract the user with a progressbar – Ňɏssa Pøngjǣrdenlarp May 08 '21 at 18:57

1 Answers1

0

Your program "hangs" while adding data to the UI because you are doing all the work in the UI thread effectively blocking the thread until your loop is done. So you need to handle this heavy duty work in a seperate thread. But you can only change the UI from the UI/main thread so something like this would throw an exception:

    private void button1_Click(object sender, EventArgs e)
    {
        new Thread(() =>
        {
            for (int i = 0; i < 4000; i++)
            {
                dataGridView1.Rows.Add(i.ToString());//throws an exception 
            }
        }).Start();
    }

The solution for this is to create a method which you can invoke the main thread to execute like so:

    private void button1_Click(object sender, EventArgs e)
    {
        new Thread(() =>
        {
            for (int i = 0; i < 10000; i++)
            {
                this.AddRow(i);
            }
        }).Start();
    }

    public void AddRow(int value)
    {
        if (this.InvokeRequired)
            this.Invoke(new MethodInvoker(() => this.AddRow(value)));
        else
            dataGridView1.Rows.Add(value.ToString());//do your ui update (add row, update progress bar etc..)
        
    }
teslae
  • 239
  • 1
  • 5