我知道很多次都会问这个问题,但我找不到合适的答案:(
我想在价格列的末尾显示egTotal Price的总和。
当值动态变化(在程序内)时,总和也应该动态改变。我无法添加自定义额外行,因为DatagridViews是数据绑定。
由于在表格布局中放置了动态行和许多Datagridviews。 (有时不会出现自定义绘制事件,因为滚动条也会出现)
我完全迷失了。有谁能建议更好的方法?
答案 0 :(得分:2)
如果您从true
采购DataGridView
,我在遇到此问题时已经创建了以下解决方案。*
想法是指出哪些列需要求和,哪个列和文本将指示“总计”标签,然后获取基础DataTable
并手动求和指示的列。这些总和和标签用于创建一个新的DataRow,它被添加到表的末尾。当进行任何更改时 - 删除新行,删除行,排序 - 旧总和行的程序性添加将被删除,然后计算新的行。
DataGridView类
DataTable
<强>用法强>
只需使用此类替换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
{
/// <summary>
/// Column index for the sum label.
/// </summary>
private int labelColumnIndex = -1;
/// <summary>
/// Text for the sum label.
/// </summary>
private string labelColumnText = string.Empty;
/// <summary>
/// Constructor. Initialize sort direction and subscribe event.
/// </summary>
public DataTableSumSortableDGV()
: base()
{
this.SumColumnIndices = new ObservableCollection<int>();
this.Direction = string.Empty;
this.AllowUserToAddRows = false;
this.AllowUserToAddRowsChanged += DataTableSumSortableDGV_AllowUserToAddRowsChanged;
this.DataBindingComplete += DataTableSumSortableDGV_DataBindingComplete;
this.DataSourceChanged += DataTableSumSortableDGV_DataSourceChanged;
this.SumColumnIndices.CollectionChanged += SumColumnIndices_CollectionChanged;
}
/// <summary>
/// Text for the sum label.
/// </summary>
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);
}
}
/// <summary>
/// Column index for the sum label.
/// </summary>
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);
}
}
/// <summary>
/// Column indices for the sum(s).
/// </summary>
public ObservableCollection<int> SumColumnIndices { get; set; }
/// <summary>
/// The DataTable sort direction.
/// </summary>
private string Direction { get; set; }
/// <summary>
/// The DataTable source.
/// </summary>
private DataTable DataTable { get; set; }
/// <summary>
/// The DataTable sum row.
/// </summary>
private DataRow SumRow { get; set; }
/// <summary>
/// DataGridView Sort method.
/// If DataSource is DataTable, special sort the source.
/// Else normal sort.
/// </summary>
/// <param name="dataGridViewColumn">The DataGridViewColumn to sort by header click.</param>
/// <param name="direction">The desired sort direction.</param>
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);
}
}
/// <summary>
/// DataBindingComplete event handler.
/// Add the sum row when DataSource = a new DataTable.
/// </summary>
/// <param name="sender">This DataGridView object.</param>
/// <param name="e">The DataGridViewBindingCompleteEventArgs.</param>
private void DataTableSumSortableDGV_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
this.DataTable = (DataTable)this.DataSource;
this.AddInitialSumRow();
}
/// <summary>
/// For a new DataSource, start with a fresh SumRow.
/// </summary>
/// <param name="sender">The DataGridView object.</param>
/// <param name="e">The EventArgs.</param>
void DataTableSumSortableDGV_DataSourceChanged(object sender, EventArgs e)
{
this.SumRow = null;
}
/// <summary>
/// Prevent users from adding a row as this is DataSourced and rows should be added to the DataTable instead.
/// </summary>
/// <param name="sender">The DataGridView object.</param>
/// <param name="e">The EventArgs.</param>
private void DataTableSumSortableDGV_AllowUserToAddRowsChanged(object sender, EventArgs e)
{
if (this.AllowUserToAddRows)
{
this.AllowUserToAddRows = false;
}
}
/// <summary>
/// The sum columns have been altered. Reflect the change immediately.
/// </summary>
/// <param name="sender">The SumColumnIndices object.</param>
/// <param name="e">The NotifyCollectionChangedEventArgs.</param>
private void SumColumnIndices_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
this.AddInitialSumRow();
}
/// <summary>
/// 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.
/// </summary>
private void AddInitialSumRow()
{
if (this.HasSumColumns())
{
Action action = () =>
{
this.RemoveSumRow();
this.AddSumRow();
};
this.MakeInternalChanges(action);
}
}
/// <summary>
/// Add the sum row to the DataTable as a ReadOnly row.
/// </summary>
private void AddSumRow()
{
List<decimal> sum = this.CreateListOfSums();
List<int> exclude = new List<int>();
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<object>(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;
}
}
}
/// <summary>
/// Clear the old sort string and any set glyph directions in header cells.
/// </summary>
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;
}
/// <summary>
/// Create the items array for the new sum row.
/// </summary>
/// <param name="length">The array length for the items.</param>
/// <param name="sum">The list of sums.</param>
/// <param name="exclude">The list of sum columns that aren't actually sum columns.</param>
/// <returns>Object array for the sum row.</returns>
private object[] CreateItemsArray(int length, List<decimal> sum, List<int> 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;
}
/// <summary>
/// Create a list of sums for each sum column index.
/// </summary>
/// <returns>A new list of sums.</returns>
private List<decimal> CreateListOfSums()
{
List<decimal> sum = new List<decimal>();
foreach (int index in this.SumColumnIndices)
{
sum.Add(0m);
}
return sum;
}
/// <summary>
/// Determine if the index is a valid column for the label.
/// </summary>
/// <returns>True if the index is valid.</returns>
private bool IsValidIndex()
{
return
this.LabelColumnIndex >= 0 &&
this.LabelColumnIndex < this.DataTable.Columns.Count &&
this.DataTable.Columns[this.LabelColumnIndex].DataType == typeof(string);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="operation">The internal changes to be made to the DataSource.</param>
private void MakeInternalChanges(Action operation)
{
this.DataBindingComplete -= DataTableSumSortableDGV_DataBindingComplete;
operation();
this.DataBindingComplete += DataTableSumSortableDGV_DataBindingComplete;
}
/// <summary>
/// Remove any existing sum row.
/// </summary>
private void RemoveSumRow()
{
if (this.SumRow != null)
{
this.DataTable.Rows.Remove(this.SumRow);
}
}
/// <summary>
/// Determine if the grid has sum sortable columns.
/// </summary>
/// <returns>
/// True if the source and sum column(s) exist.
/// False if any one condition fails = sort as normal DataGridView.
/// </returns>
private bool HasSumColumns()
{
return this.DataTable != null && this.SumColumnIndices.Count > 0;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="sort">The "ColumnName ASC/DESC" sort string.</param>
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";
,但我个人不喜欢这种方法的外观。