Is there a way to add a column footer in a datagridview
which is not databound? I am using it to take user input for adding inventory. Currently I am using a la
I ran into the same problem previously and after a long search I realised;
Winform Datagridview do not support adding footer to it.
I tried adding an extra row that could hold the summary but still did not work out fine.
You can create a user control that has two grids and with the lower grid holding the summary.
Solution--
My solution that used data binding.(1)-I Create an abstract object Item with (Name, Cost) properties.(2)-I Create a Concrete item i.e ConcItem that inherit Item(3)-I create a footer item i.e FooterItem that also inherits Item(4)-A collection of Items i.e ItemList where you instantiate the footer item.(5) Finally, just before you do data binding call the method that adds the footer item.
public abstract class Item
{
public virtual string Name { get; set; }
public virtual int Cost { get; set; }
}
public class ConcItem:Item
{
public override string Name { get; set; }
public override int Cost { get; set; }
}
public class FooterItem:Item
{
public override string Name { get { return "Total"; } }
public override int Cost { get; set; }
}
public class ItemList : List<Item>
{
private Item _footer;
public void SetFooter()
{
_footer = new FooterItem();
foreach (var item in this)
{
_footer.Cost += item.Cost;
}
this.Add(_footer);
}
}
public partial class Form1 : Form
{
Item _item;
ItemList _itemList;
public Form1()
{
InitializeComponent();
dgv.DataBindingComplete += dgv_DataBindingComplete;
_itemList = new ItemList();
SetSampleData();
}
private void SetSampleData()
{
_item = new ConcItem();
_item.Name = "Book";
_item.Cost = 250;
_itemList.Add(_item);
_item = new ConcItem();
_item.Name = "Table";
_item.Cost = 500;
_itemList.Add(_item);
_item = new ConcItem();
_item.Name = "PC";
_item.Cost = 700;
_itemList.Add(_item);
dgv.DataSource = null;
_itemList.SetFooter(); //Add the footer item b4 data binding
dgv.DataSource = _itemList;
}
void dgv_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
//If you want to do some formating on the footer row
int rowIndex = dgv.Rows.GetLastRow(DataGridViewElementStates.Visible);
if (rowIndex <= 0)
{
return;
}
dgv.Rows[rowIndex].DefaultCellStyle.BackColor = Color.Red;
dgv.Rows[rowIndex].DefaultCellStyle.SelectionBackColor = Color.Red;
dgv.Rows[rowIndex].DefaultCellStyle.Font = new Font("Microsoft Sans Serif", 12f, FontStyle.Bold);
}
}
There is a better solution. In this solution, every time the cell content changed, the value will be recalculated and displayed.
My data grid view looks like this.
In this solution, you can have any number of columns and any number of rows.
private void updateDataGridViewTotal(DataGridView dgv)
{
DataTable dt = (DataTable)dgv.DataSource;
int lastRow = (dgv.Rows.Count - 1);
if (dt.Rows[lastRow][0].ToString() == "Total")
{
dt.Rows.RemoveAt(lastRow);
}
int[] tot = new int[dt.Columns.Count];
Array.Clear(tot, 0, tot.Length);
foreach (DataRow row in dt.Rows)
{
int rowSum = 0;
for (int i = 1; i < dt.Columns.Count - 1; i++)
{
tot[i] += Convert.ToInt32(row[i]);
rowSum += Convert.ToInt32(row[i]);
}
row["Total"] = rowSum;
}
DataRow newRow = dt.NewRow();
newRow["Agent"] = "Total";
for (int i = 1; i < tot.Length; i++)
{
newRow[i] = tot[i];
}
dt.Rows.Add(newRow);
dgv.DataSource = dt;
}
Just pass your datagridview to the function.
In one of my applications I solved it by taking advantage of NewRow of DataGridView like this.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.dataGridView1.CellFormatting += dataGridView1_CellFormatting;
this.dataGridView1.CellValueChanged += dataGridView1_CellValueChanged;
}
void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex != this.dataGridView1.NewRowIndex && e.ColumnIndex == 2)
{
this.dataGridView1.InvalidateRow(this.dataGridView1.NewRowIndex);
}
}
void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.RowIndex == this.dataGridView1.NewRowIndex)
{
e.CellStyle.Font = new Font(e.CellStyle.Font, FontStyle.Bold);
e.CellStyle.ForeColor = Color.Red;
switch (e.ColumnIndex)
{
case 0:
e.Value = "Total";
break;
case 2:
var sum = 0.0d;
for (int i = 0; i < this.dataGridView1.NewRowIndex; i++)
{
var value = this.dataGridView1[2, i].Value;
if (value is double)
{
sum += ((double)value);
}
}
e.Value = Math.Round(sum, 2);
break;
// Single line version of case 2 would be
// e.Value = this.dataGridView1.Rows.Cast<DataGridViewRow>().Where(a => a.Index != a.DataGridView.NewRowIndex).Select(a => (double)a.Cells[2].Value).Sum().ToString("N2");
}
}
}
}
}
Here is live screenshot of how it works.