在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
用法
只需用此类替换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
,但我个人不喜欢这种方法的外观。