我想在 DataGridView
中添加公式单元格。是否有任何自定义的 DataGridView
可以实现这一点?
示例:
grid[4, column].Text = string.Format("=MAX({0}6:{0}{1})", columnAsString, grid.RowCount);
我想在 DataGridView
中添加公式单元格。是否有任何自定义的 DataGridView
可以实现这一点?
示例:
grid[4, column].Text = string.Format("=MAX({0}6:{0}{1})", columnAsString, grid.RowCount);
DataGridView
中,而是在其他地方,比如一个 DataTable
或某种集合(如 List<T>
)。控件只是向用户呈现数据的视图。DataTable
中。
DataColumn
分配一个 表达式。请查阅链接以获取支持的表达式类型、关键字、运算符和函数。以下示例将创建一个基于表达式的列,用于计算某些行的 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
在行级别上起作用。没有一个类似于总计行的所有行/表级计数器部分 - 这是因为数据通常来自DataSource
。添加计算行可能会意外地向该源添加新数据(如 DB)。但在代码中做到这一点并不难:
CellFormatting
答案,您可以响应来自DataTable
的事件,例如RowChanged
。在那里,您可以执行任何操作并更新表。...create table and columns
...populate table
// hook up event
dtSample.RowChanged += RowChanged;
然后在事件中,代码计算总体每单位平均值并显示在最后一行。在某些情况下,您可以使用DataTable
的Compute()
方法。与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;
}
“销售”列是通过一个“表达式”自动维护的,这意味着您不能手动更改该列。有没有自定义的DataGridView可以做到这一点?
离题了,但如果您正在寻找自定义控件,请查看Free .NET Spreadsheet Control。此外,它还支持formula。
如果编写计算代码是您的选择
如果编写计算代码是您的选择,您可以使用DataGridView
的CellFormatting
事件并在其中放置计算逻辑来计算基于其他单元格的值的单元格的值。还要处理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
,它适用于您在DataGridView
中使用的任何DataSource
,您可以在此解决方案中使用任何类型的数据源。
DataGridView
:绑定到 List<T>
,绑定到 DataTable
,没有 DataSource
并使用 RowCount
和 ColumnCount
创建列和行,没有 DataSource
并使用 Columns.Add
和 Rows.Add
创建列和行,在所有情况下都能正常工作。我怀疑你把 grid.CellFormatting += grid_CellFormatting;
放在一个不运行的位置。例如,你可能忘记将 Form1_Load
分配给 Form
的 Load
事件。 - Reza AghaeiCellEndEdit
,它仅适用于可编辑的网格,如果您的网格是只读的,则不需要它。 - Reza AghaeiPropertyGrid
并为控件分配事件处理程序。同时,您也可以在代码中实现此功能。但请不要同时使用代码和设计模式,因为事件将会被附加两次。 - Reza Aghaei不,你正在处理纯数据字符串,你需要在后台运行一个线程来执行计算并相应地更新UI。
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
DataTable
并将其绑定到DataGridView
。 - Alexander Petrov