как динамически получить сумму столбца datagridview в текстовом поле

Я хочу получить сумму столбца datagridview и показать это в текстовом поле. После каждого ввода сумма должна динамически изменяться. Для этого я использую событие textChanged текстового поля, но когда запись сделана, она не показывает никакого результата. Я хочу получить результат динамически в текстовом поле. Я хочу избежать использования кнопки для суммы.

Вот фрагмент кода для события textChanged текстового поля:

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();
        }

person Haider Khattak    schedule 13.11.2013    source источник
comment
Когда вы хотите обновить его в текстовом поле? TextChanged не то место   -  person Sriram Sakthivel    schedule 13.11.2013
comment
я хочу, чтобы textBox9 автоматически вычислял сумму независимо от того, сколько строк находится в Datagridview...   -  person Haider Khattak    schedule 14.11.2013
comment
предположим, я хочу взять сумму первого столбца... когда я добавляю значение = 2 в столбец, textBox9 автоматически получает 2, когда следующее значение добавляется в столбец, он добавляет значение к первому и так далее...   -  person Haider Khattak    schedule 14.11.2013


Ответы (5)


У вас правильный код, но вместо этого я поместил его в событие CellValueChanged и проверил, чтобы убедиться, что это та же самая ячейка, которую вы ищете. Это будет запущено, если значение ячейки изменится или если будет добавлена ​​новая строка, а затем будет присвоено значение.

    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;
    }

Я разделил вашу логику на отдельный метод, поэтому, если вы когда-нибудь захотите получить сумму для чего-то еще, вы можете просто вызвать этот метод. Я полагаю, вы могли бы просто прочитать textbox.Text, но...

Я также использовал Double.TryParse, поскольку он обеспечивает некоторую базовую защиту, если значение в данной ячейке на самом деле не является числом. Я не знаю, каков источник данных (пользователь ввел / из внешнего источника?), поэтому я решил добавить небольшую защиту.

Изменить Грант поднимает важную тему, отредактировал мое решение, включив в него измененное значение ячейки. Я использовал событие CellValueChanged вместо CellEndEdit, если что-то, кроме пользователя, может изменить его значение.

person sab669    schedule 13.11.2013
comment
это сработало, но когда сделана первая запись, в текстовом поле отображается значение 0, а не значение записи... когда отправка записи является текстовым полем, отображается значение первой записи, когда третья затем добавляется... как будто это на 1 шаг позади ... - person Haider Khattak; 14.11.2013
comment
@HaiderKhattak Я полагаю, что это произошло потому, что общая сумма генерировалась до того, как в ячейке было установлено значение. Если вы поставите точку останова в цикле for, вы можете сами проверить свойство Value. Я также изменил свой код, чтобы он только полагался на событие CellValueChanged, что может быть лучше. Попробуйте это вместо этого. - person sab669; 14.11.2013
comment
хорошо, извините, я не обновил страницу и не увидел изменений в коде... теперь все заработало... спасибо... - person Haider Khattak; 14.11.2013
comment
Нет проблем, рад, что это сработало. Возможно, вы захотите смешать мой ответ с ответом Гранта. Его код обеспечивает более сложную защиту от неверных данных в ячейке. - person sab669; 14.11.2013
comment
это менее эффективно, если у нас много строк, каждый раз, когда ячейка изменяется, мы должны перебирать все строки только для обновления суммы. - person King King; 14.11.2013
comment
Я не знаю, видели ли вы мой исходный пост до того, как я его отредактировал, но изначально я использовал событие RowsAdded, но затем Грант поднял вопрос об изменении значения. Я знал о проблеме, которую вы подняли, но не могу придумать способ избежать этой проблемы. - person sab669; 14.11.2013
comment
@ sab669 RowsAdded работает только при добавлении новой строки, когда значение ячейки изменяется, единственным подходящим событием для использования является CellValueChanged, но для обновления суммы, не зная последнего значения измененной ячейки, нам обязательно нужно снова пройтись по всем строкам. . Смотрите мой ответ для решения этой проблемы. - person King King; 14.11.2013
comment
@KingKing О, теперь я понимаю, что ты имеешь в виду. Итак, если есть 10 миллионов строк, и они меняют одну, необходимо пересчитать их все, где, как у вас, будет вычтено старое значение из общего количества и добавлено новое? Не уверен, что правильно понял, новичку читать сложно. - person sab669; 14.11.2013
comment
@ sab669 правильно. Таким образом, нам нужно всего 2 вычисления (одно вычитание и одно сложение), чтобы обновить сумму, цикл нужен только один раз (в начале). - person King King; 14.11.2013
comment
Хм, какой будет код для использования события? Вот что я сделал в дизайнере: this.dataGridView1.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellValueChanged); - person puretppc; 14.01.2014
comment
@puretppc Если вы вручную добавили обработчик событий, вы должны просто скопировать и вставить код, который я отправил в своем ответе. Затем он должен просто перейти к этому событию, которое, в свою очередь, вызывает метод. Просто нужно переименовать мое событие в dataGridView1_CellValueCh‌​anged вместо dataGridView2... - person sab669; 14.01.2014

это лучший простой способ:

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");
}
person Rufuss Carnegie    schedule 09.08.2014
comment
Почему это лучший простой способ? Объяснять - person rayryeng; 09.08.2014
comment
Rufuss - это действительно помогает и слишком просто, потому что вам нужно просто вызвать этот метод внутри метода загрузки сетки. - person Bharath theorare; 15.08.2016

Если вам нужно принять во внимание возможность того, что пользователь может редактировать существующие значения в строках сетки, вы можете подписаться на событие CellEndEdit.

Здесь я проверяю индекс столбца, чтобы вы не пересчитывали сумму, если редактируются какие-либо другие несвязанные столбцы. (Я также проверяю ввод и устанавливаю значение 0, если, скажем, кто-то вводит буквы.)

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();
    }
}

У других могут быть лучшие решения... это только одна возможность.

person Grant Winney    schedule 13.11.2013

Вы должны обработать событие CellValueChanged. Однако ваш подход немного плох, потому что каждый раз, когда значение ячейки изменяется, вам нужно перебирать все строки, чтобы обновить сумму. Вы должны отследить изменение своей ячейки, чтобы сохранить последнее значение, после чего вы сможете легко обновить сумму. Сумма должна быть рассчитана изначально с использованием цикла. Это просто сделано 1 раз.

//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());
  }
}

ПРИМЕЧАНИЕ: я предполагаю, что столбец, который вы хотите суммировать, называется sumColumn, мы должны использовать Name для ссылки на столбец, потому что он более удобочитаем. Приведенный выше код не учитывает случаи добавления новых строк и удаления существующих строк из сетки. Фактически, при программном добавлении и удалении вы должны легко обновлять sum непосредственно перед добавлением и удалением. Для добавления мы можем обработать событие RowsAdded. Все следующие регистрации обработчиков событий должны быть помещены в какой-нибудь Form.Load обработчик событий:

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();
};

Однако для удаления строк это очень сложно. RowsRemoved работает плохо, мы больше не можем ссылаться на удаленные строки в обработчике событий RowsRemoved, поэтому нам, возможно, придется пройтись по всем строкам и обновить сумму:

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

Это для удаления строк как кодом, так и пользователем. Другой вариант, который лучше, - вам нужно обрабатывать UserDeletingRow (но это работает только для удаления строк пользователем, а не кодом). Чтобы обрабатывать удаление строк по коду, я думаю, вы всегда можете вставить код, обновляющий sum, перед любым кодом, удаляющим строки, вот он:

//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();
}

Обратите внимание, что все обработчики событий должны быть зарегистрированы после того, как ваша сетка была инициализирована некоторыми начальными данными после sum инициализации.

person King King    schedule 13.11.2013

person    schedule
comment
Это действительно просто менее сложный вариант ответа Гранта. Он по-прежнему подвержен проблеме, на которую указал Кинг Кинг (что, если значение изменено программно?). Мой ответ решает эту проблему, но, как он снова указал, мое решение работает медленнее, потому что оно срабатывает для каждой ячейки в строке при добавлении новой строки. Вы по крайней мере разбиваете логику на отдельный метод, но он также восприимчив к типам, отличным от Double, в ячейке. - person sab669; 14.11.2013