In this Grid is used to stable in a Editable mode at page load. See the below Example.
First We Copy and Paste a BulkEditGridView.cs in App_Code Folder.
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Web;
namespace RealWorld.Grids
{
/// <summary>
/// BulkEditGridView allows users to edit multiple rows of a gridview at once, and have them
/// all saved.
/// </summary>
[
DefaultEvent("SelectedIndexChanged"),
Designer("System.Web.UI.Design.WebControls.GridViewDesigner, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"),
ControlValueProperty("SelectedValue"),
]
public class BulkEditGridView : System.Web.UI.WebControls.GridView
{
//key for the RowInserting event handler list
public static readonly object RowInsertingEvent = new object();
private List<int> dirtyRows = new List<int>();
private List<int> newRows = new List<int>();
/// <summary>
/// Default Constructor
/// </summary>
public BulkEditGridView()
{
}
/// <summary>
/// Modifies the creation of the row to set all rows as editable.
/// </summary>
/// <param name="rowIndex"></param>
/// <param name="dataSourceIndex"></param>
/// <param name="rowType"></param>
/// <param name="rowState"></param>
/// <returns></returns>
protected override GridViewRow CreateRow(int rowIndex, int dataSourceIndex, DataControlRowType rowType, DataControlRowState rowState)
{
return base.CreateRow(rowIndex, dataSourceIndex, rowType, rowState | DataControlRowState.Edit);
}
public List<GridViewRow> DirtyRows
{
get
{
List<GridViewRow> drs = new List<GridViewRow>();
foreach (int rowIndex in dirtyRows)
{
drs.Add(this.Rows[rowIndex]);
}
return drs;
}
}
/// <summary>
/// Adds event handlers to controls in all the editable cells.
/// </summary>
/// <param name="row"></param>
/// <param name="fields"></param>
protected override void InitializeRow(GridViewRow row, DataControlField[] fields)
{
base.InitializeRow(row, fields);
foreach (TableCell cell in row.Cells)
{
if (cell.Controls.Count > 0)
{
AddChangedHandlers(cell.Controls);
}
}
}
/// <summary>
/// Adds an event handler to editable controls.
/// </summary>
/// <param name="controls"></param>
private void AddChangedHandlers(ControlCollection controls)
{
foreach (Control ctrl in controls)
{
if (ctrl is TextBox)
{
((TextBox)ctrl).TextChanged += new EventHandler(this.HandleRowChanged);
}
else if (ctrl is CheckBox)
{
((CheckBox)ctrl).CheckedChanged += new EventHandler(this.HandleRowChanged);
}
else if (ctrl is DropDownList)
{
((DropDownList)ctrl).SelectedIndexChanged += new EventHandler(this.HandleRowChanged);
}
////could add recursion if we are missing some controls.
//else if (ctrl.Controls.Count > 0 && !(ctrl is INamingContainer) )
//{
// AddChangedHandlers(ctrl.Controls);
//}
}
}
/// <summary>
/// This gets called when a row is changed. Store the id of the row and wait to update
/// until save is called.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void HandleRowChanged(object sender, EventArgs args)
{
GridViewRow row = ((Control)sender).NamingContainer as GridViewRow;
if (null != row)
{
if (0 != (row.RowState & DataControlRowState.Insert))
{
int altRowIndex = this.InnerTable.Rows.GetRowIndex(row);
if (false == newRows.Contains(altRowIndex))
newRows.Add(altRowIndex);
}
else
{
if (false == dirtyRows.Contains(row.RowIndex))
dirtyRows.Add(row.RowIndex);
}
}
}
/// <summary>
/// Setting this property will cause the grid to update all modified records when
/// this button is clicked. It currently supports Button, ImageButton, and LinkButton.
/// If you set this property, you do not need to call save programatically.
/// </summary>
[IDReferenceProperty(typeof(Control))]
public string SaveButtonID
{
get
{
return (string)(this.ViewState["SaveButtonID"] ?? String.Empty);
}
set
{
this.ViewState["SaveButtonID"] = value;
}
}
/// <summary>
/// Attaches an eventhandler to the onclick method of the save button.
/// </summary>
/// <param name="e"></param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//Attach an event handler to the save button.
if (false == string.IsNullOrEmpty(this.SaveButtonID))
{
Control btn = RecursiveFindControl(this.NamingContainer, this.SaveButtonID);
if (null != btn)
{
if (btn is Button)
{
((Button)btn).Click += new EventHandler(SaveClicked);
}
else if (btn is LinkButton)
{
((LinkButton)btn).Click += new EventHandler(SaveClicked);
}
else if (btn is ImageButton)
{
((ImageButton)btn).Click += new ImageClickEventHandler(SaveClicked);
}
}
}
}
/// <summary>
/// Looks for a control recursively up the control tree. We need this because Page.FindControl
/// does not find the control if we are inside a masterpage content section.
/// </summary>
/// <param name="namingcontainer"></param>
/// <param name="controlName"></param>
/// <returns></returns>
private Control RecursiveFindControl(Control namingcontainer, string controlName)
{
Control c = namingcontainer.FindControl(controlName);
if (c != null)
return c;
if (namingcontainer.NamingContainer != null)
return RecursiveFindControl(namingcontainer.NamingContainer, controlName);
return null;
}
/// <summary>
/// Handles the save event, and calls the save method.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SaveClicked(object sender, EventArgs e)
{
this.Save();
this.DataBind();
}
/// <summary>
/// Saves any modified rows. This is called automatically if the SaveButtonId is set.
/// </summary>
public void Save()
{
try
{
foreach (int row in dirtyRows)
{
//TODO: need to check if we really want false here. Probably want to pull this
//fron the save button.
this.UpdateRow(row, false);
}
foreach (int row in newRows)
{
//Make the datasource save a new row.
this.InsertRow(row, false);
}
}
finally
{
dirtyRows.Clear();
newRows.Clear();
}
}
/// <summary>
/// Prepares the <see cref="RowInserting"/> event and calls insert on the DataSource.
/// </summary>
/// <param name="rowIndex"></param>
/// <param name="causesValidation"></param>
private void InsertRow(int rowIndex, bool causesValidation)
{
GridViewRow row = null;
if ((!causesValidation || (this.Page == null)) || this.Page.IsValid)
{
DataSourceView dsv = null;
bool useDataSource = base.IsBoundUsingDataSourceID;
if (useDataSource)
{
dsv = this.GetData();
if (dsv == null)
{
throw new HttpException("DataSource Returned Null View");
}
}
GridViewInsertEventArgs args = new GridViewInsertEventArgs(rowIndex);
if (useDataSource)
{
if ((row == null) && (this.InnerTable.Rows.Count > rowIndex))
{
row = this.InnerTable.Rows[rowIndex] as GridViewRow;
}
if (row != null)
{
this.ExtractRowValues(args.NewValues, row, true, false);
}
}
this.OnRowInserting(args);
if (!args.Cancel && useDataSource)
{
dsv.Insert(args.NewValues, new DataSourceViewOperationCallback(DataSourceViewInsertCallback));
}
}
}
/// <summary>
/// Callback for the datasource's insert command.
/// </summary>
/// <param name="i"></param>
/// <param name="ex"></param>
/// <returns></returns>
private bool DataSourceViewInsertCallback(int i, Exception ex)
{
if (null != ex)
{
throw ex;
}
return true;
}
/// <summary>
/// Fires the <see cref="RowInserting"/> event.
/// </summary>
/// <param name="args"></param>
protected virtual void OnRowInserting(GridViewInsertEventArgs args)
{
Delegate handler = this.Events[RowInsertingEvent];
if (null != handler)
handler.DynamicInvoke(this, args);
}
/// <summary>
/// Event fires when new row has been edited, and save is clicked.
/// </summary>
public event GridViewInsertEventHandler RowInserting
{
add
{
this.Events.AddHandler(RowInsertingEvent, value);
}
remove
{
this.Events.RemoveHandler(RowInsertingEvent, value);
}
}
/// <summary>
/// Access to the GridView's inner table.
/// </summary>
protected Table InnerTable
{
get
{
if (false == this.HasControls())
return null;
return (Table)this.Controls[0];
}
}
/// <summary>
/// Enables inline inserting. Off by default.
/// </summary>
public bool EnableInsert
{
get
{
return (bool)(this.ViewState["EnableInsert"] ?? false);
}
set
{
this.ViewState["EnableInsert"] = value;
}
}
/// <summary>
/// We have to recreate our insert row so we can load the postback info from it.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void OnPagePreLoad(object sender, EventArgs e)
{
base.OnPagePreLoad(sender, e);
if (this.EnableInsert && this.Page.IsPostBack)
{
this.CreateInsertRow();
}
}
/// <summary>
/// After the controls are databound, add a row to the end.
/// </summary>
/// <param name="e"></param>
protected override void OnDataBound(EventArgs e)
{
if (this.EnableInsert)
{
this.CreateInsertRow();
}
base.OnDataBound(e);
}
/// <summary>
/// Creates the insert row and adds it to the inner table.
/// </summary>
protected virtual void CreateInsertRow()
{
GridViewRow row = this.CreateRow(this.Rows.Count, -1, DataControlRowType.DataRow, DataControlRowState.Insert);
DataControlField[] fields = new DataControlField[this.Columns.Count];
this.Columns.CopyTo(fields, 0);
this.InitializeRow(row, fields);
int index = this.InnerTable.Rows.Count - (this.ShowFooter ? 1 : 0);
this.InnerTable.Rows.AddAt(index, row);
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public delegate void GridViewInsertEventHandler(object sender, GridViewInsertEventArgs args);
/// <summary>
///
/// </summary>
public class GridViewInsertEventArgs : CancelEventArgs
{
private int _rowIndex;
private IOrderedDictionary _values;
public GridViewInsertEventArgs(int rowIndex)
: base(false)
{
this._rowIndex = rowIndex;
}
/// <summary>
/// Gets a dictionary containing the revised values of the non-key field name/value
/// pairs in the row to update.
/// </summary>
public IOrderedDictionary NewValues
{
get
{
if (this._values == null)
{
this._values = new OrderedDictionary();
}
return this._values;
}
}
/// <summary>
/// Gets the index of the row being updated.
/// </summary>
public int RowIndex { get { return this._rowIndex; } }
}
}
We need to Register this Namespace in our Aspx page
see the Example of EditGrid.aspx Page,
- Css is used for Gridview Fixed columns
- AlignDiv Script is Used for Maintaining the ScrollPosition, it is called in code behind "EditGrid.aspx.cs" file
- Remaining Scripts are used to validate the Text box in a editable Grid.
- In this Editable Grid, We may give fixed columns and bind value. Here, i gave only
for serial no.
<%@ Page Language="C#" MasterPageFile="~/PFP.master" AutoEventWireup="true" CodeFile="EditGrid.aspx.cs" Inherits="PayRoll_EditGrid" Title="Untitled Page" %>
<%@ Register Namespace="RealWorld.Grids" TagPrefix="ac" %>
<asp:Content ID="cntEdit" ContentPlaceHolderID="cphPFPMaster" Runat="Server">
<style type="text/css">
<%--Fixed Columns in Grid-- %>
.GridViewStyle td.locked
{
background-color: #2D96CE;
font-family: Tahoma;
font-size: 8pt;
vertical-align: middle;
padding-left: 2pt;
padding-right: 2pt;
padding-top: 0pt;
padding-bottom: 0pt;
text-align: left;
height: 17px;
position: relative;
border-collapse: collapse;
border-left-color: Silver;
border-top-style: none;
border-top-color: Silver;
cursor: default;
left: expression(this.offsetParent.scrollLeft-1);
z-index: 100;
}
.GridViewStyle th
{
font-family: Tahoma;
font-size: 8pt;
vertical-align: middle;
text-align: left;
padding-left: 2pt;
padding-right: 2pt;
padding-top: 0pt;
padding-bottom: 0pt;
empty-cells: show;
position: relative;
height: 27px;
border-collapse: collapse;
border-left-color: Silver;
border-top-style: none;
top: expression(this.offsetParent.scrollTop-1);
z-index: 10;
}
.GridViewStyle th.locked
{
font-family: Tahoma;
font-size: 8pt;
vertical-align: middle;
text-align: left;
padding-left: 2pt;
padding-right: 2pt;
padding-top: 0pt;
padding-bottom: 0pt;
empty-cells: show;
position: relative;
height: 17px;
border-collapse: collapse;
border-top-style: none;
left: expression(this.offsetParent.scrollLeft-1);
z-index: 150;
}
.GVHeaderStyle
{
font-family: Tahoma;
font-size: 8pt;
vertical-align: middle;
background-color: #7193ae;
color: #000000;
font-weight: bold;
padding: 0pt;
empty-cells: show;
position: relative;
border-left-style: none;
border-collapse: collapse;
border-left-color: Silver;
top: expression(this.offsetParent.scrollTop-1);
left: expression(this.offsetParent.scrollLeft-1);
height: 13px;
z-index: 1000;
}
</style>
<script language="javascript" type="text/javascript">
//For Maintaining Intial Scroll Position
function AlignDiv(obj, objColumn) {
var objDiv = document.getElementById('<%= divdatagrid.ClientID%>');
if (objColumn == obj - 1) {
objDiv.scrollLeft = 0;
}
}
//For Numeric Value Validation
function extractNumber(obj, decimalPlaces, allowNegative) {
var temp = obj.value;
// avoid changing things if already formatted correctly
var reg0Str = '[0-9]*';
if (decimalPlaces > 0) {
reg0Str += '\\.?[0-9]{0,' + decimalPlaces + '}';
} else if (decimalPlaces < 0) {
reg0Str += '\\.?[0-9]*';
}
reg0Str = allowNegative ? '^-?' + reg0Str : '^' + reg0Str;
reg0Str = reg0Str + '$';
var reg0 = new RegExp(reg0Str);
if (reg0.test(temp)) return true;
// first replace all non numbers
var reg1Str = '[^0-9' + (decimalPlaces != 0 ? '.' : '') + (allowNegative ? '-' : '') + ']';
var reg1 = new RegExp(reg1Str, 'g');
temp = temp.replace(reg1, '');
if (allowNegative) {
// replace extra negative
var hasNegative = temp.length > 0 && temp.charAt(0) == '-';
var reg2 = /-/g;
temp = temp.replace(reg2, '');
if (hasNegative) temp = '-' + temp;
}
if (decimalPlaces != 0) {
var reg3 = /\./g;
var reg3Array = reg3.exec(temp);
if (reg3Array != null) {
// keep only first occurrence of .
// and the number of places specified by decimalPlaces or the entire string if decimalPlaces < 0
var reg3Right = temp.substring(reg3Array.index + reg3Array[0].length);
reg3Right = reg3Right.replace(reg3, '');
reg3Right = decimalPlaces > 0 ? reg3Right.substring(0, decimalPlaces) : reg3Right;
temp = temp.substring(0, reg3Array.index) + '.' + reg3Right;
}
}
obj.value = temp;
}
//Allow Only Numbers
function blockNonNumbers(obj, e, allowDecimal, allowNegative) {
var key;
var isCtrl = false;
var keychar;
var reg;
if (window.event) {
key = e.keyCode;
isCtrl = window.event.ctrlKey
} else if (e.which) {
key = e.which;
isCtrl = e.ctrlKey;
}
if (isNaN(key)) return true;
keychar = String.fromCharCode(key);
// check for backspace or delete, or if Ctrl was pressed
if (key == 8 || isCtrl) {
return true;
}
reg = /\d/;
var isFirstN = allowNegative ? keychar == '-' && obj.value.indexOf('-') == -1 : false;
var isFirstD = allowDecimal ? keychar == '.' && obj.value.indexOf('.') == -1 : false;
return isFirstN || isFirstD || reg.test(keychar);
}
</script>
<table id="tblgvWorkType" runat="server" align="center" style="width: 50%; height: 100%;">
<tr>
<td align="left">
<div id="divdatagrid" class="GridViewStyle" style="overflow: auto; position: relative;
clear: none; width: 370px;" runat="server" >
<ac:BulkEditGridView ID="gvWorkType" runat="server" CssClass="mGrid" DataKeyNames="Name,Email"
OnRowDataBound="gvWorkType_RowDataBound">
<HeaderStyle HorizontalAlign="Center" BackColor="#2D96CE" ForeColor="White" />
<RowStyle HorizontalAlign="Left" ForeColor="White" />
<AlternatingRowStyle HorizontalAlign="Left" ForeColor="White" />
<Columns>
<asp:TemplateField HeaderText="#" >
<ItemTemplate>
<asp:Label ID="lblgvSeqid" ForeColor="Navy" runat="server" Text='<% # Container.DataItemIndex + 1 %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</ac:BulkEditGridView>
<br />
<asp:Button ID="btnSave" runat="server" Text="Save" CssClass="ThemeButtons" OnClick="btnSave_Click" />
</div>
</td>
</tr>
</table>
</asp:Content>
In the Code Behind, I have created the Dynamic Table and bind into the Grid. See the below "EditGrid.aspx.cs" File,
using System;
using System.Data;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class PayRoll_EditGrid : System.Web.UI.Page
{
// I have create the Dynamic Table and Bind It
protected void Page_Load(object sender, EventArgs e)
{
DataTable objDt = new DataTable();
DataColumn dcEdit = null;
dcEdit = new DataColumn();
dcEdit.DataType = System.Type.GetType("System.String");
dcEdit.ColumnName = "Name";
objDt.Columns.Add(dcEdit);
DataColumn dcEdit2 = null;
dcEdit2 = new DataColumn();
dcEdit2.DataType = System.Type.GetType("System.String");
dcEdit2.ColumnName = "Email";
objDt.Columns.Add(dcEdit2);
DataColumn dcEdit3 = null;
dcEdit3 = new DataColumn();
dcEdit3.DataType = System.Type.GetType("System.String");
dcEdit3.ColumnName = "Age";
objDt.Columns.Add(dcEdit3);
DataColumn dcEdit4 = null;
dcEdit4 = new DataColumn();
dcEdit4.DataType = System.Type.GetType("System.String");
dcEdit4.ColumnName = "Qualification";
objDt.Columns.Add(dcEdit4);
DataRow dr = objDt.NewRow();
dr["Name"] = "Suresh";
dr["Email"] = "nsuresh@hovservices.in";
dr["Age"] = "";
objDt.Rows.Add(dr);
DataRow dr1 = objDt.NewRow();
dr1["Name"] = "vijayan";
dr1["Email"] = "vijayans@hovservices.in";
dr1["Age"] = "";
objDt.Rows.Add(dr1);
gvWorkType.DataSource = objDt;
gvWorkType.DataBind();
}
// At the Time of DB Load Get the Values from GridView
protected void btnSave_Click(object sender, EventArgs e)
{
//Get Values
foreach (GridViewRow gr in gvWorkType.Rows)
{
foreach (Control ctl in gr.Controls)
{
if (ctl is TextBox)
{
string str = (ctl as TextBox).Text;
}
}
}
}
/*
In RowDataBound, Lock the Columns Using The Css
Adding the Script event for needed columns and ForeColor here.
*/
protected void gvWorkType_RowDataBound(object sender, GridViewRowEventArgs e)
{
//Make Un scrollable
for (int ColumLock = 0; ColumLock < 3; ColumLock++)
{
e.Row.Cells[ColumLock].CssClass = "locked";
}
if (e.Row.RowType == DataControlRowType.DataRow)
{
//Set the Cell color...
e.Row.Cells[1].ForeColor = e.Row.Cells[2].ForeColor = System.Drawing.Color.Navy;
//Get the each TextBox and add the validations
for (int column = 3; column < e.Row.Cells.Count; column++)
foreach (Control ctrl in e.Row.Cells[column].Controls)
{
if (ctrl.GetType() == typeof(TextBox))
{
TextBox txt = ctrl as TextBox;
txt.Width = 55;
txt.MaxLength = 5;
//http://www.mredkj.com/tutorials/validate2.html --- For Reference
txt.Attributes.Add("onblur", "extractNumber(this,0,false);");
txt.Attributes.Add("onkeyup", "extractNumber(this,0,false);");
txt.Attributes.Add("onkeypress", "return blockNonNumbers(this, event, false, false);");
txt.Attributes.Add("class", "Controls");
if (column == e.Row.Cells.Count - 1)
txt.Attributes.Add("onblur", "return AlignDiv('" + e.Row.Cells.Count + "','" + column + "')");
}
}
}
}
}
Now we run the Code, we will get the Editable grid like below,
This is used only in the situation of bulk entry with dynamic columns
06:31 |
Category:
ASP.NET
|
1 comments
Comments (1)
Found yours after hours of failed attempt to implement an editable grid with frozen headers/columns. The freezing was not working especially since the edit was using Jquery .
Got it working with your example.
Thanks !