Introduction
Have you ever wanted to add editable rows to a GridView
but didn't want the hassle with customizing the footer or manipulating the data source? If that's the case, maybe the NewRowGridView
is something for you.
Using the code
Inside the demo project, you'll find several files, from which the NewRowGridView
is the most important one. Here, we extend the known GridView
component with extra editable rows using the GridView
's own methods for creating those. We expose the newly created rows with the NewRows
property. We also add a property NewRowCount
which determines the number of editable rows to add to the GridView
. When a user changes the data in the newly created rows, we can access those through the NewRowsChanged
property.
private GridViewRowCollection _newRows;
private List< int > _changedRows;
[Browsable(false)]
public GridViewRowCollection NewRows
{
get
{
return (this._newRows == null) ? new
GridViewRowCollection(new ArrayList()) :
this._newRows;
}
}
public int NewRowCount
{
get
{
object viewState = this.ViewState["NewRowCount"];
return (viewState == null) ? 0 : (int)viewState;
}
set { this.ViewState["NewRowCount"] = value; }
}
[Browsable(false)]
public GridViewRowCollection NewRowsChanged
{
get
{
if (this._changedRows != null)
{
ArrayList changedRows = new ArrayList();
foreach (int rowIndex in this._changedRows)
{
changedRows.Add(this._newRows[rowIndex]);
}
return new GridViewRowCollection(changedRows);
}
return new GridViewRowCollection(new ArrayList());
}
}
For creating rows, we'll be using the GridView
's CreateRow
and InitializeRow
methods. The CreateRow
method expects, amongst others, the DataControlRowState
property. By setting this to Insert
, the row will be initialized with editable components like a TextBox
or a CheckBox
. The data information will be collected through the GetDataFields
method.
private DataControlField[] GetDataControlFields()
{
DataControlField[] fields =
new DataControlField[this.Columns.Count];
base.Columns.CopyTo(fields, 0);
return fields;
}
private GridViewRow CreateNewRow(int rowIndex, DataControlField[] fields)
{
GridViewRow newRow = base.CreateRow(rowIndex, -1,
DataControlRowType.DataRow,
DataControlRowState.Insert);
base.InitializeRow(newRow, fields);
this.AddRowChanged(newRow);
return newRow;
}
The AddRowChanged
method adds an event handler to the newly created controls within the new row which fires when the state of these controls change. We then add the RowIndex
of the new row to the _changedRow
list for further reference.
private void AddRowChanged(Control control)
{
foreach (Control ctr in control.Controls)
{
if (ctr is TextBox)
{
((TextBox)ctr).TextChanged +=
new EventHandler(this.RowChanged);
}
else if (ctr is ListControl)
{
((ListControl)ctr).SelectedIndexChanged +=
new EventHandler(this.RowChanged);
}
else if (ctr is CheckBox)
{
((CheckBox)ctr).CheckedChanged +=
new EventHandler(this.RowChanged);
}
if (ctr.HasControls())
{
this.AddRowChanged(ctr);
}
}
}
private void RowChanged(object sender, EventArgs e)
{
GridViewRow row =
(GridViewRow)((Control)sender).NamingContainer;
if (this._changedRows == null)
{
this._changedRows = new List< int >();
}
if (!this._changedRows.Contains(row.RowIndex))
{
this._changedRows.Add(row.RowIndex);
}
}
The GridView
, when rendered to the browser, is basically nothing more than a Table
. We can find the table by calling the Parent
property of one of the rows, or in case of no data, we create our own by calling the CreateChildTable
method of the GridView
. In the latter case, we also have to add the table to the GridView
's ControlCollection
. Another point of interest: if there's no data (rows), the GridView
will automatically hide the header and footer. In that case, we have to create a header row in the same way we created an insertable row, except that the DataControlRowState
property is set to Normal
.
private void CreateRows()
{
if (this.NewRowCount > 0)
{
ArrayList list = new ArrayList();
DataControlField[] fields = this.GetDataControlFields();
for (int i = 0; i < this.NewRowCount; i++)
{
GridViewRow newRow = this.CreateNewRow(i, fields);
list.Add(newRow);
}
this._newRows = new GridViewRowCollection(list);
Table grid;
if (this.Rows.Count == 0)
{
grid = this.CreateChildTable();
this.Controls.Add(grid);
if (this.ShowHeader)
{
GridViewRow headerRow =
this.CreateHeaderRow(fields);
grid.Rows.Add(headerRow);
}
}
else
{
grid = (Table)this.Rows[0].Parent;
}
int rowIndex = this.Rows.Count + 1;
foreach (GridViewRow newRow in this._newRows)
{
grid.Rows.AddAt(rowIndex, newRow);
rowIndex++;
}
}
}
private GridViewRow CreateHeaderRow(DataControlField[] fields)
{
GridViewRow headerRow = base.CreateRow(-1, -1,
DataControlRowType.Header, DataControlRowState.Normal);
base.InitializeRow(headerRow, fields);
return headerRow;
}
The last step is to call the CreateRows
method. Therefore, we have to override the CreateChildControls
method.
protected override int CreateChildControls(IEnumerable
dataSource, bool dataBinding)
{
int rowCount = base.CreateChildControls(dataSource,
dataBinding);
this.CreateNewRows();
return rowCount;
}
That's it! Now you can access the new values through the NewRows
collection, and the changed values through the NewRowsChanged
collection.