4

I want to get the sum of a column of datagridview and show that in textbox. After each entry the sum should be changed dynamically. For that I am using textChanged event of a textbox but when the entry is made it does not show any result. I want to get the result dynamically in the textbox. I want to avoid the use of button for sum.

Here is a piece of code for the textbox textChanged event:

private void textBox9_TextChanged(object sender, EventArgs e)
        {
            double sum = 0;
            for (int i = 0; i < dataGridView2.Rows.Count; ++i)
            {
                sum += Convert.ToDouble(dataGridView2.Rows[i].Cells[5].Value);
            }
            textBox9.Text = sum.ToString();
        }
David Yaw
  • 27,383
  • 4
  • 60
  • 93
Haider Khattak
  • 567
  • 3
  • 8
  • 32
  • 1
    When you want to update it in textbox? `TextChanged` is not the right place – Sriram Sakthivel Nov 13 '13 at 19:56
  • i want that textBox9 calculates the sum automatically for no matter how many rows are there in the Datagridview... – Haider Khattak Nov 13 '13 at 20:01
  • suppose i want to take the sum of first coulmn...when i add the value=2 in coulmn textBox9 gets 2 automatically when the next value is added in the column it add the value with the first one and so on... – Haider Khattak Nov 13 '13 at 20:02

5 Answers5

10

You've got your code right, but instead I've put it in the CellValueChanged event and check to make sure it was the same cell you're looking for. This will get fired if the cell's value changes, or if a new row is added and subsequently gets a value assigned.

    private void dataGridView2_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
        if (e.ColumnIndex == 5)
            textBox9.Text = CellSum().ToString();
    }

    private double CellSum()
    {
        double sum = 0;
        for (int i = 0; i < dataGridView2.Rows.Count; ++i)
        {
            double d = 0;
            Double.TryParse(dataGridView2.Rows[i].Cells[5].Value.ToString(), out d);
            sum += d;
        }
        return sum;
    }

I split up your logic into a separate method, so incase you ever want to get the sum for something else, you can simply call that method. I suppose you could just read textbox.Text, but...

I've also used Double.TryParse as it provides some basic protection incase the value in that given cell is not actually a number. I don't know what the source of the data is (user entered / from an outside source?) so I figured I'd add a little protection.

Edit Grant raises a great point, editted my solution to include cell value being modified. I used the CellValueChanged event instead of CellEndEdit, incase something other than the user can change its value.

sab669
  • 3,984
  • 8
  • 38
  • 75
  • this worked but when the first entry is made the text box shows the value 0 not the value of the entry...when send entry is textbox shows the value of first entry when third then it adds...like it is 1 step behind... – Haider Khattak Nov 13 '13 at 20:23
  • @HaiderKhattak I believe that was because the total was being generated *before* the cell was having a value set. If you put a breakpoint in the for loop, you can check the `Value` property yourself. I've also modified my code to **only** rely on the CellValueChanged event which might be better. Try that instead. – sab669 Nov 13 '13 at 20:28
  • well sorry i did not refreshed the page and did not saw the changes in the code... well it worked now...thanks... – Haider Khattak Nov 13 '13 at 20:30
  • No problem, glad it worked. You might want to kind of blend my answer with Grant's answer though. His code provides some more sophisticated protection against bad data in the cell. – sab669 Nov 13 '13 at 20:31
  • this is less effective if we have many rows, each time a cell is changed, we have to loop through all the rows just to update the sum. – King King Nov 13 '13 at 20:42
  • I don't know if you saw my original post before I edited it, but I was using a `RowsAdded` event initially but then Grant brought up the point of the value changing. I was aware of the issue you raised but cannot think of a way to avoid the problem. – sab669 Nov 13 '13 at 20:50
  • @sab669 RowsAdded works only if adding new row, when the cell value is changed, the only suitable event to use is `CellValueChanged` but to update the sum without knowing the last value of the changed cell, we surely have to loop through all the rows again. See my answer for the solution to this problem. – King King Nov 13 '13 at 20:54
  • @KingKing Ohhh, I see what you mean now. So if there's 10M rows and they change one, it needs to recount them all where as yours will sort of subtract the old value from the total and add the new one? Not sure if I got that right, tough to read as a novice. – sab669 Nov 13 '13 at 21:00
  • @sab669 that's right. That way we need just 2 calculations (one subtraction and one addition) to update the sum, the loop is needed only once (at the beginning). – King King Nov 13 '13 at 21:01
  • Uhm what would be the code for using the event? This is what I did in the designer: `this.dataGridView1.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellValueChanged);` – puretppc Jan 13 '14 at 20:40
  • @puretppc If you've manually added an event handler you should just be able to copy-paste the code I submitted in my answer. It should then just jump to that event which in turn calls the method. Just need to rename my event to `dataGridView1_CellValueCh‌​anged` instead of `dataGridView2...` – sab669 Jan 14 '14 at 14:09
3

this is the best simple way :

private void your_name()
{

    Double your_variable = dataGridView1.Rows.Cast<DataGridViewRow>().Sum(x => Convert.ToDouble(x.Cells["Column4"].Value));

    this.textBox1.Text = your_variable.ToString("N2");
}
Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
2

If you need to take into account the possibility that a user may edit existing values in the grid rows, you can subscribe to the CellEndEdit event.

Here, I'm checking the column index so you're not recalculating the sum if any other unrelated columns are edited. (I'm also validating the input and setting the value to 0 if, say, someone enters letters.)

private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex == 5)
    {
        decimal result;
        if (!Decimal.TryParse(Convert.ToString(dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value), out result))
            dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = 0;

        textBox1.Text = dataGridView1.Rows.Cast<DataGridViewRow>()
                                     .Sum(x => Convert.ToDecimal(x.Cells[5].Value))
                                     .ToString();
    }
}

Others may have better solutions... this is just one possibility.

Grant Winney
  • 65,241
  • 13
  • 115
  • 165
1

You have to handle the event CellValueChanged. However your approach is a little bad because each time a cell's value changes, you have to loop through all the rows to update the sum. You should track your cell change to store the last value, then you can update the sum easily. The sum needs to be calculated initially using the loop. It's just done 1 time.

//This is used as CellTemplate for your interested column
public class TrackedValueDataGridViewCell : DataGridViewTextBoxCell {
    public object OldValue { get; set; }
    protected override bool SetValue(int rowIndex, object value) {
      OldValue = Value;                
      return base.SetValue(rowIndex, value);
    }
}

public partial class Form1 : Form {
  public Form1(){
    InitializeComponent();
    //init your grid
    dataGridView1.DataSource = yourDataSource;
    dataGridView1.Columns["sumColumn"].CellTemplate = new TrackedValueDataGridViewCell();
    sum = InitSum(dataGridView1,"sumColumn");
    textBox9.Text = sum.ToString();
    dataGridView1.CellValueChanged += (s,e) => {
      if(dataGridView1.Columns[e.ColumnIndex].Name != "sumColumn") return;
      var cell = ((TrackedValueDataGridViewCell) dataGridView1[e.ColumnIndex, e.RowIndex]);
      sum -= ((double?) cell.OldValue).GetValueOrDefault();
      sum += ((double?)cell.Value).GetValueOrDefault();
      textBox9.Text = sum.ToString();
    };

  }
  double sum;
  public double InitSum(DataGridView grid, string colName) {
      return grid.Rows.OfType<DataGridViewRow>()                       
                 .Sum(row => ((double?) row.Cells[colName].Value).GetValueOrDefault());
  }
}

NOTE: I suppose the column you want to sum up is named sumColumn, we should use Name to reference column because it's more readable. The code above doesn't count the cases when adding new rows and removing existing rows from grid. In fact, whenever adding and removing programmatically, you should update the sum easily right before adding and removing. For adding we can handle the RowsAdded event. All the following event handler registrations should be placed in some Form.Load event handler:

dataGridView1.RowsAdded += (s, e) => {
    for (int i = e.RowIndex; i < e.RowIndex + e.RowCount; i++) {
        sum += ((double?)dataGridView1["sumColumn", i].Value).GetValueOrDefault();
    }
    textBox9.Text = sum.ToString();
};

However for removing rows, it's very tricky. The RowsRemoved doesn't work well, we can't refer the removed rows anymore in the RowsRemoved event handler, so we may have to loop through all the rows and update the sum:

dataGridView1.RowsRemoved += (s, e) => {
  sum = InitSum(dataGridView1,"sumColumn");
  textBox9.Text = sum.ToString();      
};

That's for removing rows by both code and user. Another option which is better is you have to handle the UserDeletingRow (but this works only for removing rows by user, not by code). To handle removing rows by code, I think you can always insert code updating the sum before any code removing the rows, here it is:

//for user removing rows
dataGridView1.UserDeletingRow += (s, e) => {
  sum -= ((double?) e.Row.Cells["sumColumn"].Value).GetValueOrDefault();
  textBox9.Text = sum.ToString();
};
//for code removing rows, use some method to remove rows like this:
public void RemoveRows(DataGridView grid, int startIndex, int count){
  for(int i = startIndex; i <= startIndex + count; i++){
     //update the sum first
     sum -= ((double?)grid.Rows[i].Cells["sumColumn"].Value).GetValueOrDefault();
     //then remove the row
     grid.Rows.RemoveAt(startIndex);
  }
  textBox9.Text = sum.ToString();
}

Note that all the event handlers should be registered after your grid has been initialized with some initial data following by the sum initialization.

King King
  • 61,710
  • 16
  • 105
  • 130
1
private void dataGridView2_CellEndEdit(object sender, DataGridViewCellEventArgs e)
    {
        if (e.ColumnIndex == 5)
        {
            summition();
        }
    }
    void summition() 
    { 
        double sum = 0;
        foreach (DataGridViewRow row in dataGridView2.Rows)
        {
            if(!row .IsNewRow )
                sum += Convert.ToDouble(row.Cells [5].Value .ToString () );
        }


        textBox9.Text = sum.ToString();
    }
  • This really is just a less-sophisticated variation of Grant's answer. It's still susceptible to the issue King King pointed out (what if the value is changed programatically?). My answer addresses that issue, but as he again pointed out, my solution becomes slower because it's firing for every cell in a row when a new row is added. You at least break up the logic into a separate method, but it's also susceptible to non-Double types in the cell. – sab669 Nov 13 '13 at 20:54