Подтвердить что ты не робот

Применить формулу Cell в DataGridview

Я хочу добавить ячейку формулы в DataGridView. Есть ли какой-либо пользовательский DataGridView для этого?

Пример:

grid[4, column].Text = string.Format("=MAX({0}6:{0}{1})", columnAsString, grid.RowCount);
4b9b3361

Ответ 1

Есть ли какой-либо пользовательский DataGridView для этого?

Вне темы, но если вы ищете настраиваемый элемент управления, посмотрите Бесплатный контроль таблиц .NET.. Также он поддерживает formula.

Если код для расчета является для вас вариантом

Если код для вычисления является для вас вариантом, для вычисления значения ячейки на основе значений некоторых других ячеек вы можете использовать событие CellFormatting DataGridView и поместить там логику расчета. Также обрабатывайте CellEndEdit и вызывайте InvalidateCell или Invalidate, чтобы принудительно обновить значение ячейки после каждого из опорных ячеек.

Вот пример:

void Form1_Load(object sender, EventArgs e)
{
    Random r = new Random();
    var dt = new DataTable();
    dt.Columns.Add("A", typeof(int));
    dt.Columns.Add("B", typeof(int));
    for (int i = 0; i < 10; i++)
        dt.Rows.Add(r.Next(100));
    grid.DataSource = dt;
    grid.CellFormatting += grid_CellFormatting;
    grid.CellEndEdit += grid_CellEndEdit;
}
void grid_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    grid.Invalidate();
}
void grid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
    var grid = sender as DataGridView;
    var parameterColumnName = "A";       //Parameter column name
    var start = 0;                       //Start row index for formula
    var end = grid.RowCount - 1;         //End row index for formula
    var resultRowIndex = 0;              //Result row index
    var resultColumnName = "B";          //Result column name
    if (e.RowIndex == resultRowIndex &&
        grid.Columns[e.ColumnIndex].Name == resultColumnName)
    {
        var list = Enumerable.Range(start, end - start + 1)
              .Select(i => grid.Rows[i].Cells[parameterColumnName].Value)
              .Where(x => x != null && x != DBNull.Value)
              .Cast<int>();
        if (list.Any())
            e.Value = list.Max();
    }
}

Примечание

Решение не ограничивается DataTable, оно будет работать независимо от DataSource, которое вы используете для DataGridView, и вы можете использовать любой источник данных в этом решении.

Ответ 2

Во многих случаях данные на самом деле не находятся в DataGridView, но в другом месте, например, DataTable или какой-либо коллекции (например, List<T>). Элемент управления просто представляет пользователю представление данных.

Есть несколько способов сделать что-то в соответствии с тем, что вы хотите. Для обоих из них данные фактически находятся в DataTable.

Выражения

A DataColumn может быть назначен Expression. Обратитесь к ссылке для типов выражений, ключевых слов, операторов и поддерживаемых функций. Ниже будут созданы столбцы, основанные на выражениях, для нескольких Quantity * Price для некоторых строк:

dtSample = new DataTable();
dtSample.Columns.Add(new DataColumn("Item", typeof(string)));
dtSample.Columns.Add(new DataColumn("Quantity", typeof(int)));
dtSample.Columns.Add(new DataColumn("Price", typeof(decimal)));
dtSample.Columns.Add(new DataColumn("Sale", typeof(decimal)));

// assign expression using the col names
dtSample.Columns[3].Expression = "(Quantity * Price)";

После добавления некоторых случайных данных, а также пустой строки, DataTable будет поддерживать эти столбцы для вас. Это работает так, как вы, вероятно, хотите: если пользователь (или код) изменяет значение ячейки Quantity или Price, содержимое столбца Sale автоматически обновляется. (изображение позже после второго метода).

Expressions работают на уровне строк. Не существует счетной части all-rows/table-wise для чего-то вроде строки TOTALS - это потому, что данные часто бывают от DataSource. Добавление вычисленных строк может случайно добавить новые данные в этот источник (например, БД). Но это не сложно сделать в коде:

Расчеты с событиями

Как и в ответе DGV CellFormatting, вы можете отвечать на события из DataTable, например RowChanged. Там вы можете выполнять любые операции и обновлять таблицу.

...create table and columns
...populate table
// hook up event
dtSample.RowChanged += RowChanged;

Затем в этом случае код вычисляет по всей единице измерения для отображения в последней строке. В некоторых случаях вы можете использовать метод Compute() для DataTable. В отличие от Expression, он не обновляется автоматически и как показано в этом ответе, он может быть неуклюжим для обновления.

С типизированными данными в DataTable довольно легко выполнить вычисления в ответ на события:

private void RowChanged(object sender, DataRowChangeEventArgs e)
{
    // number of rows used 
    int Rows = dtSample.Rows.Count-1;
    if (e.Row == dtSample.Rows[Rows]) return;

    // display TotalSales / TotalUnits
    // get the units
    int TotUnits = dtSample
        .AsEnumerable()
        .Where(r => !r.IsNull("Quantity"))
        .Take(Rows)
        .Sum(n => n.Field<int>("Quantity"));

    // sum Sales, divide and display in DGV
    dtSample.Rows[Rows]["Price"] = dtSample
        .AsEnumerable()
        .Where(r => !r.IsNull("Sale"))
        .Take(Rows)
        .Sum(n => n.Field<decimal>("Sale")) / TotUnits;
}

введите описание изображения здесь

Столбец "Продажи" автоматически поддерживается через Expression, что означает, что вы не можете вручную ничего сделать в этом столбце.

Общая средняя цена снизу также "автоматически" обновляется, разница в том, что нам пришлось написать небольшое количество кода для этого.

Ответ 3

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

Ответ 4

Несколько вещей, которые вы можете попробовать:

Обработайте событие изменения значения ячейки, чтобы определить, когда изменяется одна из ячеек, используемых в вычислении, и рассчитать соответствующее значение. Как то так (попробуйте в новом проекте)

Public Class Form1
  Private WithEvents DGV As New DataGridView
  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    DGV.SetBounds(20, 50, 400, 200)
    Controls.Add(DGV)
    DGV.Columns.Add("Qty", "Qty")
    DGV.Columns.Add("Price", "Price")
    DGV.Columns.Add("Total", "Total")
    DGV.Columns("Total").ReadOnly = True
    DGV.RowCount = 5
  End Sub

  Private Sub DGV_CellValueChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DGV.CellValueChanged
    If e.ColumnIndex = DGV.Columns("Qty").Index Or e.ColumnIndex = DGV.Columns("Price").Index Then
      DGV("Total", e.RowIndex).Value = CInt(DGV("Qty", e.RowIndex).Value) * CDbl(DGV("Price", e.RowIndex).Value)
    End If
  End Sub
End Class

Или вместо того, чтобы читать файл непосредственно в сетке данных, считайте его в таблицу данных и свяжите таблицу с таблицей данных. Затем вы можете добавить столбец выражений в таблицу данных, чтобы выполнить расчет. Что-то вроде этого:

Public Class Form1
  Private WithEvents DGV As New DataGridView
  Private DT As New DataTable
  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    DGV.SetBounds(20, 50, 400, 200)
    Controls.Add(DGV)
    DT.Columns.Add("Qty", GetType(Int32))
    DT.Columns.Add("Price", GetType(Double))
    DT.Columns.Add("Total", GetType(Double))
    DT.Columns("Total").Expression = "Qty * Price"
    DGV.DataSource = DT
  End Sub
End Class