Click here to Skip to main content
15,851,335 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm trying to show progress bar as much as the amount of records marked in the checkbox column. I am selecting the datas to be saved with checkboxes by clicking. I want to set the progressbar speed at the rate of the amount of data to be saved, but I couldn't. My progressbar is at the same speed either recording one data or 30 datas recordings.

What I have tried:

C#
if (dataGridView1.Rows.Count > 100)

            {

                int total = 500; /

                for (int i = 0; i <= total; i++)                 {

                    System.Threading.Thread.Sleep(100);

                    int percents = (i * 100) / total;

                    backgroundWorker1.ReportProgress(percents, i);

                }

            }

            else if (dataGridView1.Rows.Count < 100)

            {

                int total = 100;

 

                for (int i = 0; i <= total; i++)

                {

                    System.Threading.Thread.Sleep(50);

                    int percents = (i * 100) / total;

                    backgroundWorker1.ReportProgress(percents, i);

                }

            }
Posted
Updated 13-Aug-23 2:44am
v3
Comments
Dave Kreskowiak 12-Aug-23 14:49pm    
Quote:I want to set the progressbar speed at the rate of the amount of data to be saved.
What does that mean?
Member 12505620 12-Aug-23 17:22pm    
It means that I want to set the progressbar speed at the rate of the amount of data that will be saved into the database. For example If I check five checkboxes that will be saved into the database, the save speed should be faster than fifteen datas that will be saved. So the recording speed should be proportional to the number of data.
Dave Kreskowiak 12-Aug-23 22:00pm    
That still doesn't make any sense.

The progress bar should be updated whenever appropriate through the process of updating the data. NOBODY gives a damn at what speed the progressbar "progresses".

If you've got 5 items to update, the progressbar gets updated 5 times, upon the completion of the update for each item.

It'f you've updating thousands of rows, you're going to have to do a little math to map that count of rows to the 0 to 100 range the progressbar expects.
Graeme_Grant 12-Aug-23 22:12pm    
"NOBODY gives a damn" ... lol

Do not try and work out the time it takes, every computer is different and also depends on the load on your CPU at the time of running.

Here is how to work with the progress bar:
1. Count the number of rows and store in a float or double variable. This is for the calculation to come as we will be working with decimals.
2. Iterate over the rows counting how many completed
3. Set the progress position to position / total (ie: percentage completed). If you want an integer value, then multiply the result * 100
4. When completed, set the progress to 100%, just in case of rounding errors

Here is a working example with 100,000 rows:
C#
public partial class Form1 : Form
{
    private List<Data> SampleData = new List<Data>();

    public Form1()
    {
        InitializeComponent();

        Load += Form1_Load;
        button1.Click += button1_Click;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        double totalRow = dataGridView1.RowCount;

        for (int i = 0; i < dataGridView1.RowCount; i++)
        {
            progressBar1.Value = (int)(i / totalRow * 100);
            dataGridView1.Rows[i].Cells[1].Value = 
                !(bool)dataGridView1.Rows[i].Cells[1].Value;
        }

        progressBar1.Value = 100;
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        LoadData();

        dataGridView1.DataSource = SampleData;
        
        dataGridView1.Columns[0].AutoSizeMode = 
            DataGridViewAutoSizeColumnMode.None;

        dataGridView1.Columns[0].Width = 150;
    }

    private void LoadData()
    {
        for (int i = 0; i < 100000; i++)
        {
            SampleData.Add(new Data {Title = $"A random title # {i:N0}"});
        }
    }
}

public class Data
{
    public string Title { get; set; }
    public bool Selected { get; set; }
}

If you want to try it, start a new project, add a DataGridView, Button, and a ProgressBar to a Form, then drop the code in the form's code behind, and run.

UPDATE #1
I noticed that you're trying to do it in the background on a seperate thread. I've updated the sample and used Async/Await (Introduction To Async, Await, And Tasks | Microsoft Learn[^]), not a BackgroundWorker.
C#
public partial class Form1 : Form
{
    private List<Data> SampleData = new List<Data>();

    public Form1()
    {
        InitializeComponent();

        Load += Form1_Load;
        button1.Click += button1_Click;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        ProcessAsync().ConfigureAwait(false);
    }

    private async Task ProcessAsync()
    {
        double totalRow = dataGridView1.RowCount;

        for (int i = 0; i < dataGridView1.RowCount; i++)
        {
            int index = i;

            DispatcherHelper.Execute(() =>
            {
                progressBar1.Value = (int)(index / totalRow * 100);
  
                dataGridView1.Rows[index].Cells[1].Value =
                    !(bool)dataGridView1.Rows[index].Cells[1].Value;
            });

            await Task.Delay(10); // simulate workload
        }

        DispatcherHelper.Execute(() => progressBar1.Value = 100);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        LoadData();

        dataGridView1.DataSource = SampleData;
        
        dataGridView1.Columns[0].AutoSizeMode = 
            DataGridViewAutoSizeColumnMode.None;

        dataGridView1.Columns[0].Width = 150;
    }

    private void LoadData()
    {
        for (int i = 0; i < 1000; i++)
        {
            SampleData.Add(new Data {Title = $"A random title # {i:N0}"});
        }
    }
}

public class Data
{
    public string Title { get; set; }
    public bool Selected { get; set; }
}

public static class DispatcherHelper
{
    public static void Execute(Action action)
    {
        // no cross-thread concerns
        if (Application.OpenForms.Count == 0)
        {
            action.Invoke();
            return;
        }

        try
        {
            if (Application.OpenForms[0].InvokeRequired)
                // Marshall to Main Thread
                Application.OpenForms[0]?.Invoke(action);
            else
                // We are already on the Main Thread
                action.Invoke();
        }
        catch (Exception)
        {
            // ignore as might be thrown on shutting down
        }
    }
}

The original sample code all ran on the UI thread, so the form was not movable. The Async/await sample code fixes that. When you run the code and hit the button, you can see the ProgressBar updating, move the form around.

UPDATE #2

If you want to use a BackgroundWorker | Microsoft Learn[^] , then here is another version of the sample code that does:
C#
public partial class Form1 : Form
{
    private List<Data> SampleData = new List<Data>();

    private BackgroundWorker worker = new BackgroundWorker();

    public Form1()
    {
        InitializeComponent();

        Load += Form1_Load;
        button1.Click += button1_Click;

        worker.WorkerReportsProgress = true;
        worker.DoWork += Worker_DoWork;
        worker.ProgressChanged += Worker_ProgressChanged;
        worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
    }

    private void Worker_DoWork(object sender, DoWorkEventArgs e)
    {
        double totalRow = dataGridView1.RowCount;

        for (int i = 0; i < dataGridView1.RowCount; i++)
        {
            dataGridView1.Rows[i].Cells[1].Value = 
                !(bool)dataGridView1.Rows[i].Cells[1].Value;

            Thread.Sleep(10); // simulate workload

            worker.ReportProgress((int)(i / totalRow * 100));
        }
    }

    private void Worker_ProgressChanged(object sender,
        ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
    }

    private void Worker_RunWorkerCompleted(object sender,
        RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("Done");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        worker.RunWorkerAsync();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        LoadData();

        dataGridView1.DataSource = SampleData;
        
        dataGridView1.Columns[0].AutoSizeMode = 
            DataGridViewAutoSizeColumnMode.None;

        dataGridView1.Columns[0].Width = 150;
    }

    private void LoadData()
    {
        for (int i = 0; i < 1000; i++)
        {
            SampleData.Add(new Data {Title = $"A random title # {i:N0}"});
        }
    }
}

public class Data
{
    public string Title { get; set; }
    public bool Selected { get; set; }
}

Both the Async/Await & BackgroundWorker samples are valid. To me, IMHO, the Async/Await is cleaner and the modern method.
 
Share this answer
 
v5

I would suggest using the Progress<T> class for reporting progress. It is easy to use and takes care of all the thread marshalling for you. In the start event handler await a method that returns a Task.


C#
private async void buttonClick(object sender, EventArgs e)
 {
     //do the required work in an async Task method
     await CallDoWorkAsync();
 }

In CallDoWorkAsync,set up a new Progress<T> instance by defining a method to update the UI as required after each progress.Report(i) call. Then call await Task.Run() to run a method that will run on a thread pool thread and not on the UI.


C#
public async Task CallDoWorkAsync()
        {
            //instantiate a new Progress instance
            //and define a method to handle progress reporting
            var progress = new Progress<int>(i =>
            {
                //this is the work that gets done
                //after every progress.Report(i) call
                //in this case just update the progress bar
                progressBar.Value = (i);
            }
            );
            //this is the call to a method that will
            //run asynchronously  on its own thread
            await Task.Run(() => DoWork(progress, 500));
        }

The DoWork method does the heavy lifting asynchronously on its own thread. It calls progress.Report(i) to pass the current percentage complete value back to the UI on each iteration of the, for loop so the progress bar can be updated on the UI thread as the DoWork method progresses.


C#
private void DoWork(IProgress<int> progress, int count)
  {
      for (int i = 0; i < count; i++)
      {
          //simulate a long-running method by blocking the thread
          Thread.Sleep(10);
          //report the progress percentage,
          int percent = (int)Math.Round((i / (double)count) * 100);
          progress.Report(percent);
      }
  }
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900