DataGridView column footer c#.net winforms
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 label to display the total, but I want to change it to footer if possible.
Solution 1:
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); } }
Solution 2:
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.