Gridviews and adding empty rows for display purposes only.
On my Sites-Easy project, all of the Administrative features use Ajax, Gridviews, and ModalPopups to present a UI that is consistent. This consistency is obtained by using abstract classes and delegates so that creating new Administrative Modules is almost as simple as dragging a control into the Designer View of Visual Studio. However, since the number of records that the Gridview may display will vary - having a listing of only two records versus showing a result that pulls back whatever the page limit size is - makes the UI look incomplete. Instead, I wanted to have the Gridview maintain the same size regardless of number of records - and if number of records to display is less than page size then insert dummy rows that just fill UI space.
How to do it however? Most of all the blog entries and forum posts address this for when returning a null set of data (which causes the header and footer to dissapear) - none addressed my particular issue.
Here is my solution:
In this example the Gridview will be bound to a DataSet and I will assume you already know how to bind Gridviews to various data sources.
//BindGridView() event
//ds = some dataset
//Compare the number of rows to configured PageSize if less than then stuff new rows in
if (ds.Tables[0].Rows.Count < PageSize)
{
//Set the number of inserts to do
int itemCount = (PageSize - ds.Tables[0].Rows.Count);
//Loop
for (int itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
//Add empty row
ds.Tables[0].Rows.Add();
}
// commit the changes to the Dataset
ds.AcceptChanges();
}
//Now bind to the Gridview
grdData.DataSource = ds;
grdData.DataBind();
Now that we have Gridview populated now comes the real fun. In this example I illustrate how to also implement security checks for your Gridview buttons.
protected virtual void GridView_RowDataBound(Object s, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow )
{
//Set a Boolean to determine if our ID is not a integer.
bool isValidRow = true;
//Find our buttons / checkboxes / etc that display something other than a text field.
ImageButton btnDelete = (ImageButton)e.Row.FindControl("btnDelete");
ImageButton btnEdit = (ImageButton)e.Row.FindControl("btnEdit");
ImageButton btnClone = (ImageButton)e.Row.FindControl("btnClone");
ImageButton btnView = (ImageButton)e.Row.FindControl("btnView");
CheckBox chkSelect = (CheckBox)e.Row.FindControl("chkSelect");
//Since the "id" is a integer we must first cast it to a string and compare to see if it is not a empty string. If actual string then we
//know we have real "id" and avoid specific casting. If string.empty then there was no "id" and row is null set isValid to false.
if (DataBinder.Eval(e.Row.DataItem, "id").ToString() == string.Empty)
{
isValidRow = false;
}
//Process the id and set up desired behavior of buttons , etc...This code also checks for roles to disable any
//buttons that invoke a command argument if user does not match the roles. The role handler here is custom.
if (isValidRow)
{
int editID = (int)DataBinder.Eval(e.Row.DataItem, "id");
if (btnDelete != null)
{
btnDelete.ImageUrl = ResolveUrl("~/Communities/" + objSectionInfo.Skin + "/Images/Admin/delete.gif");
if (objUserInfo.MayDelete || Array.IndexOf(objSectionInfo.DeleteRoles, "Community-Authenticated") != -1)
{
btnDelete.CommandArgument = editID.ToString();
}
else
{ btnDelete.Enabled = false; }
}
if (btnEdit != null)
{
btnEdit.ImageUrl = ResolveUrl("~/Communities/" + objSectionInfo.Skin + "/Images/Admin/edit.gif");
if (objUserInfo.MayEdit || Array.IndexOf(objSectionInfo.EditRoles, "Community-Authenticated") != -1)
{
btnEdit.CommandArgument = editID.ToString();
}
else
{ btnEdit.Enabled = false; }
}
if (btnClone != null)
{
btnClone.ImageUrl = ResolveUrl("~/Communities/" + objSectionInfo.Skin + "/Images/Admin/clone.png");
if (objUserInfo.MayAdd|| Array.IndexOf(objSectionInfo.AddRoles, "Community-Authenticated") != -1)
{
btnClone.CommandArgument = editID.ToString();
}
else
{ btnClone.Enabled = false; }
}
if (btnView != null)
{
btnView.ImageUrl = ResolveUrl("~/Communities/" + objSectionInfo.Skin + "/Images/Admin/view.png");
if (objUserInfo.MayView || Array.IndexOf(objSectionInfo.ViewRoles, "Community-Authenticated") != -1)
{
btnView.CommandArgument = editID.ToString();
}
else
{ btnView.Enabled = false; }
}
}
// Here we HIDE any button controls because these are the empty filler / dummy rows. If you have other gridview templated items that display images or buttons - then you will need to add them here as well. If you have text just being displayed such as in your aspx or ascx as such:
//<asp:BoundField DataField="Message_Name" DataFormatString="{0:d}" HeaderText="Question" />
//NO need to handle them as they accept null values. The only exception may be a date field but not positive.
// Why not use the e.Row.Controls.Clear?
//If you do the Clear the row will be considered null and will not be displayed.
else
{
if (btnDelete != null)
{ btnDelete.Visible = false; }
if (btnEdit != null)
{ btnEdit.Visible = false; }
if (btnClone != null)
{ btnClone.Visible = false; }
if (chkSelect != null)
{ chkSelect.Visible = false; }
if (btnView != null)
{ btnView.Visible = false; }
}
}
}
This is an example screenshot of end result:
Before:

After And End Result:

Hopefully, this is a concrete example on how to insert dummy rows and additionally - using the control.Enable along with checking for user roles for activities presented.