在Excel中最后一行下方动态显示Datagridview列总和的最佳方法?

我知道很多次问这个问题,但我找不到合适的答案:(

我想在价格列的末尾显示egTotal Price的总和。

当值动态变化(在程序内)时,总和也应该动态改变。 我无法添加自定义额外行,因为DatagridViews是数据绑定。

由于在表格布局中放置了动态行和许多Datagridviews。 (自定义绘制事件不太可取,因为滚动条有时也会出现)

我完全迷失了。 有谁能建议更好的方法?

如果您从DataTable中获取DataGridView ,我在遇到此问题时已经创建了以下解决方案。*

想法是指出哪些列需要求和,哪个列和文本将指示“总计”标签,然后获取基础DataTable并手动求和指示的列。 这些总和和标签用于创建新的DataRow,它被添加到表的末尾。 当进行任何更改时 – 以编程方式添加新行,删除行,排序 – 删除旧的总和行 ,然后计算新的

DataGridView类

 using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Data; using System.Windows.Forms; namespace TestSortWithSum { public class DataTableSumSortableDGV : DataGridView { ///  /// Column index for the sum label. ///  private int labelColumnIndex = -1; ///  /// Text for the sum label. ///  private string labelColumnText = string.Empty; ///  /// Constructor. Initialize sort direction and subscribe event. ///  public DataTableSumSortableDGV() : base() { this.SumColumnIndices = new ObservableCollection(); this.Direction = string.Empty; this.AllowUserToAddRows = false; this.AllowUserToAddRowsChanged += DataTableSumSortableDGV_AllowUserToAddRowsChanged; this.DataBindingComplete += DataTableSumSortableDGV_DataBindingComplete; this.DataSourceChanged += DataTableSumSortableDGV_DataSourceChanged; this.SumColumnIndices.CollectionChanged += SumColumnIndices_CollectionChanged; } ///  /// Text for the sum label. ///  public string LabelColumnText { get { return this.labelColumnText; } set { Action action = () => { if (this.HasSumColumns()) { this.RemoveSumRow(); } this.labelColumnText = value; if (this.HasSumColumns()) { this.AddSumRow(); } }; this.MakeInternalChanges(action); } } ///  /// Column index for the sum label. ///  public int LabelColumnIndex { get { return this.labelColumnIndex; } set { Action action = () => { if (this.HasSumColumns()) { this.RemoveSumRow(); } this.labelColumnIndex = value; if (this.HasSumColumns()) { this.AddSumRow(); } }; this.MakeInternalChanges(action); } } ///  /// Column indices for the sum(s). ///  public ObservableCollection SumColumnIndices { get; set; } ///  /// The DataTable sort direction. ///  private string Direction { get; set; } ///  /// The DataTable source. ///  private DataTable DataTable { get; set; } ///  /// The DataTable sum row. ///  private DataRow SumRow { get; set; } ///  /// DataGridView Sort method. /// If DataSource is DataTable, special sort the source. /// Else normal sort. ///  /// The DataGridViewColumn to sort by header click. /// The desired sort direction. public override void Sort(DataGridViewColumn dataGridViewColumn, System.ComponentModel.ListSortDirection direction) { if (this.HasSumColumns()) { Action action = () => { this.RemoveSumRow(); string col = this.DataTable.Columns[dataGridViewColumn.Index].ColumnName; if (!this.Direction.Contains(col)) { this.ClearOldSort(); } string sort = this.Direction.Contains("ASC") ? "DESC" : "ASC"; this.Direction = string.Format("{0} {1}", col, sort); this.SortRows(this.Direction); this.AddSumRow(); }; this.MakeInternalChanges(action); dataGridViewColumn.HeaderCell.SortGlyphDirection = this.Direction.Contains("ASC") ? SortOrder.Ascending : SortOrder.Descending; } else { this.DataTable.DefaultView.Sort = string.Empty; base.Sort(dataGridViewColumn, direction); } } ///  /// DataBindingComplete event handler. /// Add the sum row when DataSource = a new DataTable. ///  /// This DataGridView object. /// The DataGridViewBindingCompleteEventArgs. private void DataTableSumSortableDGV_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e) { this.DataTable = (DataTable)this.DataSource; this.AddInitialSumRow(); } ///  /// For a new DataSource, start with a fresh SumRow. ///  /// The DataGridView object. /// The EventArgs. void DataTableSumSortableDGV_DataSourceChanged(object sender, EventArgs e) { this.SumRow = null; } ///  /// Prevent users from adding a row as this is DataSourced and rows should be added to the DataTable instead. ///  /// The DataGridView object. /// The EventArgs. private void DataTableSumSortableDGV_AllowUserToAddRowsChanged(object sender, EventArgs e) { if (this.AllowUserToAddRows) { this.AllowUserToAddRows = false; } } ///  /// The sum columns have been altered. Reflect the change immediately. ///  /// The SumColumnIndices object. /// The NotifyCollectionChangedEventArgs. private void SumColumnIndices_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { this.AddInitialSumRow(); } ///  /// Add the sum row for the first time: once the DataTable is sourced and /// the label column index, label text, and sum column index are set. ///  private void AddInitialSumRow() { if (this.HasSumColumns()) { Action action = () => { this.RemoveSumRow(); this.AddSumRow(); }; this.MakeInternalChanges(action); } } ///  /// Add the sum row to the DataTable as a ReadOnly row. ///  private void AddSumRow() { List sum = this.CreateListOfSums(); List exclude = new List(); for (int row = 0; row < this.DataTable.Rows.Count; row++) { for (int index = 0; index < this.SumColumnIndices.Count; index++) { try { int col = this.SumColumnIndices[index]; decimal value = 0; if (Decimal.TryParse(this.DataTable.Rows[row].ItemArray[col].ToString(), out value)) { sum[index] += value; } else if (!exclude.Contains(col)) { exclude.Add(col); } } catch (RowNotInTableException) { continue; } } } object[] items = this.CreateItemsArray(this.DataTable.Columns.Count, sum, exclude); if (Array.TrueForAll(items, item => { return item == null; })) { this.SumRow = null; } else { this.SumRow = this.DataTable.NewRow(); this.SumRow.ItemArray = items; this.DataTable.Rows.Add(this.SumRow); if (this.Rows.Count > 0) { this.Rows[this.Rows.Count - 1].ReadOnly = true; } } } ///  /// Clear the old sort string and any set glyph directions in header cells. ///  private void ClearOldSort() { if (!string.IsNullOrEmpty(this.Direction)) { string[] sortVals = this.Direction.Split(new char[] { ' ' }); // [ "ColName", "ASC/DESC" ] this.Columns[sortVals[0]].HeaderCell.SortGlyphDirection = SortOrder.None; } this.Direction = string.Empty; } ///  /// Create the items array for the new sum row. ///  /// The array length for the items. /// The list of sums. /// The list of sum columns that aren't actually sum columns. /// Object array for the sum row. private object[] CreateItemsArray(int length, List sum, List exclude) { object[] items = new object[length]; if (this.IsValidIndex()) { items[this.LabelColumnIndex] = this.LabelColumnText; } for (int index = 0; index < this.SumColumnIndices.Count; index++) { int col = this.SumColumnIndices[index]; if (!exclude.Contains(col)) { items[col] = sum[index]; } } return items; } ///  /// Create a list of sums for each sum column index. ///  /// A new list of sums. private List CreateListOfSums() { List sum = new List(); foreach (int index in this.SumColumnIndices) { sum.Add(0m); } return sum; } ///  /// Determine if the index is a valid column for the label. ///  /// True if the index is valid. private bool IsValidIndex() { return this.LabelColumnIndex >= 0 && this.LabelColumnIndex < this.DataTable.Columns.Count && this.DataTable.Columns[this.LabelColumnIndex].DataType == typeof(string); } ///  /// Unsubscribe the DataBindingComplete event handler, call internal sorting changes, /// then re-subscribe to the DataBindingComplete event handler. This must be done /// with any item removal/addition to the DataSource DataTable to prevent recursion /// resulting in a Stack Overflow. ///  /// The internal changes to be made to the DataSource. private void MakeInternalChanges(Action operation) { this.DataBindingComplete -= DataTableSumSortableDGV_DataBindingComplete; operation(); this.DataBindingComplete += DataTableSumSortableDGV_DataBindingComplete; } ///  /// Remove any existing sum row. ///  private void RemoveSumRow() { if (this.SumRow != null) { this.DataTable.Rows.Remove(this.SumRow); } } ///  /// Determine if the grid has sum sortable columns. ///  ///  /// True if the source and sum column(s) exist. /// False if any one condition fails = sort as normal DataGridView. ///  private bool HasSumColumns() { return this.DataTable != null && this.SumColumnIndices.Count > 0; } ///  /// Sort the DataTable by re-ordering the actual items. /// Get the sorted row order. For each sorted row, /// remove it from the original list, then re-add it to the end. ///  /// The "ColumnName ASC/DESC" sort string. private void SortRows(string sort) { DataRow[] sortedRows = this.DataTable.Select(string.Empty, sort); foreach (DataRow row in sortedRows) { object[] items = (object[])row.ItemArray.Clone(); this.DataTable.Rows.Remove(row); this.DataTable.Rows.Add(items); } } } } 

用法

只需用此类替换DataGridView的实例:

 DataTableSumSortableDGV dataGridView1 = new DataTableSumSortableDGV(); 

然后,指示您的总和列和标签列,如下所示:

 this.dataGridView1.DataSource = GoGetTheDataTable(); this.dataGridView1.SumColumnIndices.Add(3); this.dataGridView1.SumColumnIndices.Add(4); this.dataGridView1.LabelColumnIndex = 2; this.dataGridView1.LabelColumnText = "Total"; 

以上几行的顺序无关紧要,应该可行。 下面我捕获了一个演示行为中的行为的示例:

行动中的例子

*或者你当然可以添加一个TextBox ,但我个人不喜欢这种方法的外观。