Table of Contents
Introduction
While answering questions on the Question and Answer forums[^], we noticed a certain pattern to the questions being asked very frequently. Rather than trying to answer each question on an ad-hoc and
individual basis, we decided to club together a set of commonly asked questions into an article. Every time we noticed a similar pattern-based question being asked,
we will now provide a link to this article, instead of providing answers for each question. This will help us answer better and people can have a look at alternative solutions as well.
Let's start with the ASP.NET GridView
, which is an extremely flexible data control in ASP.NET. It was observed that a number of questions
pertaining to the ASP.NET GridView
were asked daily. There is a question on exporting GridView
to Excel almost everyday.
Questions on GridView
template fields, hiding specific values from a cell, changing cell values at run time, are very common as well.
Many other such patterns can be found if some time is spent in the Q&A forums.
We hope to keep this article updated by continuously adding more commonly asked questions to it.
Is it possible to have AutoGenerateColumns set to true and define columns in the "Columns" section?
Yes, it is. This is not a very common situation when you are enabling AutoGenerateColumns="true"
and defining custom columns.
GridView
creates columns for each field when AutoGenerateColumns=true
. We set AutoGenerateColumns=false
when we need
a few specific fields from the data source and need to define custom column sections with different types of columns.
What will happen if we set AutoGenerateColumns
to true and add a "Columns
" section to define columns? GridView
will generate columns
for each and every field, but before these auto generated columns, it will add columns that you define explicitly.
How do we bind a GridView DataSource with explicit columns?
GridView
allows several types of columns to bind data that can be defined explicitly. One of the most common column types is BoundField
.
Apart from a bound field, GridView
supports different types of columns which include ButtonField
, ImageField
, HyperLinkField
,
TemplateField
, etc. Let's have a quick look at how we can use a BoundField
to define GridView
columns explicitly.
BoundField
has a property called DataField
. The DataField
needs to be associated with any of the data objects
of the GridView
DataSource
.
This type column binding provides you enough opportunity to customize the columns and headers.
Formatting column values in a GridView
Another frequently asked question is how do we format a cell value? The GridView
BoundField
column provides a property "DataFormatString
"
which you can use to format data. DataFormatString
takes similar arguments as String.Format
, where it has a placeholder and a format indicator.
Here is a quick example of date formatting:
This is also very useful when we need formatting for a currency field. You can find a list of all formatting strings
over here.
How to access values of a template field in a GridView?
A GridView
TemplateField
allows us to specify custom controls, multiple fields, and HTML using a custom template. TemplateField
allows us to define
a completely customized template for a GridView
column. We can bind the data within a template control using a binding expression. ASP.NET provides a number of ways to take control
over a template control.
Let's say you have the following design in a GridView
:
You now want to access the value of this label from the code-behind where you want to update the text or you want to do some additional operations based on the value of the label
or another value. In the code snippet below, if the name starts with "A", it won't be displayed.
protected void myGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Customer cust = e.Row.DataItem as Customer;
if (!cust.ShowURL)
{
Label lableName = e.Row.FindControl("Label1") as Label;
if (lableName.Text.StartsWith("A"))
{
lableName.Visible = false;
}
}
}
}
So for any situation, if you want to access controls inside template fields, first check if the row is a DataRow
,
then use your GridViewRow.FindControl
method to get the control. Once you have the control of the same type, you can do all additional operations.
Dynamically set control visibility inside ItemTemplates of a GridView
If you want to hide a field or control based on some conditional value, you can either do it from the code-behind or your can take help of a binding expression.
If you are following the approach of code-behind, first of all, you need to find the control value during the RowDataBound
of the GridView
.
Once your have the control, you can set the visibility of the control based on the condition that you want to apply.
The example below shows hiding a LinkButton
based on some condition:
protected void myGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Customer cust = e.Row.DataItem as Customer;
if (!cust.ShowURL)
{
LinkButton lnkWebURL = e.Row.FindControl("lnk") as LinkButton;
if (lnkWebURL != null)
{
lnkWebURL.Visible = false;
}
}
}
}
Well, you can achieve the same thing in a smart way by just using a Binding Expression. We can use a Conditional Expression within a GridView
Binding Expression.
So whatever you write in the code-behind can be replaced by:
This will first evaluate the value of "ShowURL
" and then set the Visible
property of the GridView
.
This question was recently asked in Q&A forums. I answered and put a detailed blog post with an explanation
at Dynamically set control visibility
inside ItemTemplates of GridView using a Binding Expression.
Displaying custom messages / images with a NULL value in an ASP.NET GridView
When we bind some data source with a GridView
which contains some fields with a NULL
value, ASP.NET renders them as blank. But most of the time,
we need to show custom messages or images instead of a null value. You can take help of the RowDataBound
method for this and override the null value.
Instead of doing that, it is better to make use of NullDisplayText
properties to replace the null value.
This will replace all the null values with a plain text "No Data".
Once we have NullDisplayText=”No Data”
, every field for that bound field which contains a NULL
value will be replaced by “No Data”.
We can even customize the text with HTML formatting as shown below:
The output will be as below:
Original source: Displaying custom messages / images
with a NULL value in an ASP.NET GridView.
How to display “Yes” or “No” instead of a checkbox while binding a boolean value with a GridView?
If you are binding some data source in a GridView
which has a field type of Boolean
, then the GridView
renders it as a checkbox.
But sometimes we may want to display either Yes/No or “1/0” instead of a checked/unchecked box.
You can simply update the cell value on RowDataBind
and change the default value as required.
protected void GridView2_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Student s = (Student)e.Row.DataItem;
if (s.Status == true)
{
e.Row.Cells[2].Text = "1";
}
else
{
e.Row.Cells[2].Text = "0";
}
}
}
You can find the complete tips over here: How to
display “Yes” or “No” instead of a checkbox while binding a Boolean value with a GridView?
How to pass multiple values using a GridView HyperLinkField?
While working with GridView
in ASP.NET, most of the time we use a HyperlinkField
column to navigate from one page to a different page with some values as arguments.
Here, I have explained how we can pass multiple parameters with a HyperLinkField
in a GridView
. To implement this feature, you need to know about
the DataNavigationUrlField
and DataNavigateUrlFormatString
properties of HyperLinkField
. Let’s assume that you have a GridView
as given below and you have already bound the DataSource
from the code-behind.
<asp:gridview id="GrdEmp">
<columns runat="server" autogeneratecolumns="False"
cellpadding="4">
<asp:boundfield datafield="ID" headertext="ID">
<asp:boundfield datafield="Name" headertext="Name">
<asp:boundfield datafield="ParentID" headertext="Base ID">
Let’s first consider the case of passing a single parameter. The first thing that you need to do is to add a GridView
HyperLinkField
column.
After that you need to set the DataNavigationUrlField
and DataNavigateUrlFormatString
properties. In DataNavigationUrlField
,
you have to mention the name of the DataField
which you want to bind as a querystring. In DataNavigateUrlFormatString
, you have to give the formatted URL
string for the navigation. The concept is the same here like FormatString
- the only difference is the data comes from a DataNavigationUrlField
.
<asp:hyperlinkfield text="Details" datanavigateurlfields="ID"
datanavigateurlformatstring="/Details.aspx?EmpId={0}">
In the above example, I have passed an ID as a DataNavingationUrlField
. So as the GridView
binds, for each row, the corresponding ID will be bound as a URL string
with the employee ID. The code snippet for the HTML view is right below:
<a href="http://www.codeproject.com/Default.aspx?EmpID=123">Details</a>
To add multiple parameters, you need to specify multiple DataField
s with comma (,) separated within DataNavigateURLField
s and also need to provide
the DataNavigateURLFormatString
with the proper number of arguments.
asp:hyperlinkfield datanavigateurlformatstring="/Details.aspx?EmpId={0}&ParentID={1}"
datanavigateurlfields="ID,ParentID" text="Details">
This is the HTML output for a multiple parameter HyperLinkField
:
<a href="http://www.codeproject.com/Default.aspx?EmpID=123&ParentID=765">Details</a>
To summarize the post, you can use comma separated DataNavigateURLField
s to pass multiple DataField
s as parameters with the HyperlinkField
in the GridView
. Also, you need to use a properly formatted URL with DataaNavigateURLFormatString
.
Original source: How to pass multiple values using a GridView HyperLinkField?
How to pass external values with a GridView HyperLinkField which are not part of your Gridview DataSource member?
DataNavigationUrlField
uses only those fields as parameters which are part of the GridView DataSource
. Now the problem comes when you want
to pass some other variables as parameters which are not part of the DataSource
. As shown in the image below, we are passing
EmpID
and ParentId
as arguments and these two fields are the data members of the GridView DataSource
.
Now say you want to pass a ChildID
for that particular record along with ParentID
and EmpID
and you want that the hyperlink URL
should be like “Default.aspx?EmpID=1&ParentID=P1&ChildID=C1” where ChildID
is not part of the data source.
You can achieve this by writing code in the code-behind for the particular GridView
. There are two events where you can overwrite the navigation URL
of that hyperlink field. You can use Gridview_RowDataBound
or Gridview_PreRender
.
Let’s start with Gridview_RowDataBound
. The RowDataBound
event of the GridView
is raised when a data row is bound to data.
So for each and every row, the RowDataBound
event is raised to bind with actual data from the data source. On the RowDataBound
event, you can check
for the particular cell control and can append the NavigateURL
. Shown below is the code snippet:
protected void grdStudent_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
HyperLink hyperlink = (HyperLink)e.Row.Cells[0].Controls[0];
hyperlink.NavigateUrl += "&ChildID=" + this.ExternalValue;
}
}
You can do the same thing in the Gridview_PreRender
event in a similar way. As per the ASP.NET page life cycle, Pre_Render
for a control is raised
just before saving view state and the Render
event. This is the last event where you can customize your control before saving the viewstate data and rendering it.
Shown below is the code snippet:
protected void grdStudent_PreRender(object sender, EventArgs e)
{
foreach (GridViewRow row in grdStudent.Rows)
{
if (row.RowType == DataControlRowType.DataRow)
{
HyperLink grdviewLink = (HyperLink)row.Cells[0].Controls[0];
grdviewLink.NavigateUrl += "&ChildID=" + this.externalValue;
}
}
}
If you want to know how it’s actually working, just set a breakpoint during the data bound event, and you will find the NavigateURL
for that hyperlink field has already been
set with the value that you have passed as DataNavigationUrlField
. And inside RowDataBound
or Pre_Render
, we are appending the same
NavigateURL
with an external parameter.
Below is the HTML snippet for the HyperLinkField
.
You can use NavigateURL
properties for Hyperlinkfield
to set the URL, but NavigateURL
will set the same URL for all the rows.
So if you want a different URL for each and every row, you have to use DataNavigationUrlField
or you need to override NavigateURL
during
RowDataBound
or Pre_Render
. And if you set both the properties (NavigateURL
and DataNavigationUrlField
)
the DataNavigationUrlField
property takes precedence.
Export a GridView to Excel
How to export a GridView
to Microsoft Excel is one of the most common questions asked in forums.
The approach to this is fairly simple. A GridView
can be copied row by row into an HTML table. This table can be written to an HTML stream and there we have the Excel.
Let us look at some code. The UI is fairly simple with a GridView
containing some data and an Export to Excel button. This is what the UI would look like:
The UI code is just two lines and for completion's sake is shown here.
<asp:gridview runat="server" autogeneratecolumns="true"
height="66px" width="543px" style="z-index: 118" id="GridView1">
<asp:button onclick="Button1_Click" text="Export to Excel" runat="server" id="Button1">
The main code is written in the button click event. We will use the HttpContext
's Response
object.
An Excel header is added to the Response
object (with the corresponding MIME type).
A StringWriter
object is created first. A HtmlTextWriter
object is created from this StringWriter
.
An HTML Table
is then created and the GridView
is copied row by row into this table. This table is then rendered into the HtmlTextWriter
object.
The StringWriter
object is then written into the Response
and the browser is able to parse the response as an Excel file (provided it recognizes the Excel MIME type).
Here is the code:
protected void Button1_Click(object sender, EventArgs e)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-disposition",
string.Format("attachment;filename={0}.xls", "Exported"));
HttpContext.Current.Response.Charset = "";
HttpContext.Current.Response.ContentType = "application/ms-excel";
using (StringWriter sw = new StringWriter())
{
using (HtmlTextWriter htw = new HtmlTextWriter(sw))
{
Table table = new Table();
foreach (GridViewRow row in GridView1.Rows)
{
table.Rows.Add(row);
}
table.RenderControl(htw);
HttpContext.Current.Response.Write(sw.ToString());
HttpContext.Current.Response.End();
}
}
}
http://mattberseth.com/blog/2007/04/export_gridview_to_excel_1.html[^] is a good link on exporting a GridView
to Excel, and this is what was referred to while preparing this tip.
GridView Importing Excel to GridView
A common question (though not as common as export to Excel) is importing an Excel file into a GridView
. For this example, we will use a FileUpload
control
to choose an Excel file from a drive. Note that FileUpload
does not return the full file path to the server due to security reasons. The UI and its code look like this.
An Import button is provided to do the actual import to the GridView
. Binding is used to allow the GridView
to bind to imported data.
The code-behind on the Import button's Click
event is shown below.
protected void Import_Click(object sender, EventArgs e)
{
DataSet ds = new DataSet();
String strConn = @"Provider = Microsoft.ACE.OLEDB.12.0;" + @"Data Source=C:\" +
MyFile.FileName + @";Extended Properties='Excel 12.0;HDR=Yes;'";
OleDbDataAdapter da = new OleDbDataAdapter("SELECT * FROM [Sheet1$]", strConn);
da.Fill(ds);
GridView1.DataSource = ds.Tables[0].DefaultView;
GridView1.DataBind();
}
Some lines of the above code require an explanation. In this example, the Jet engine is used to read an Excel file as the database.
The connection string's provider points to the OLEDB.12.0 version (rather than the 4.0 one) because Excel 2007 is being used. Thus the Extended Properties section is different as well.
HDR is nothing but an attribute that suggests that the first row in the Excel file is to be used as a header (when set to Yes). The entire connection string would be different when connecting
to Excel 2003 or older. A DataSet
is filled using a DataAdapter
and the first table is assigned to the GridView
. That's all there is to it.
Another approach to implement 'Import to Excel' would have been to use Excel Interop. This approach is not discussed here.
Dropdown in a GridView
Adding a dropdown to a GridView
is a fairly common requirement as well.
Let's first have a look at the ASPX code. In order to define a dropdown, we will need to add ItemTemplate
s to the code. The first two columns,
Name and Age, will be simple DataBinders. The third ItemTemplate
will contain a DropDownList
. Remember, AutoGenerateColumns
has been
set to false. Fairly simple till here.
<columns>
<asp:templatefield headertext="Name">
<itemtemplate>
<%# DataBinder.Eval(Container.DataItem, "Name")>
</itemtemplate>
</asp:templatefield>
<asp:templatefield headertext="Age">
<itemtemplate>
<%# DataBinder.Eval(Container.DataItem, "Age")>
</itemtemplate>
</asp:templatefield>
<asp:templatefield sortexpression="Occupation" headertext="Occupation">
<itemtemplate>
<asp:dropdownlist id="DropDownList1" runat="server">
</itemtemplate>
</asp:templatefield>
</columns>
This is the UI the user sees on the screen:
The GridView
is populated using a DataTable
. This is fairly easy as well.
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Age");
dt.Columns.Add("Occupation");
DataRow dr = dt.NewRow();
dr["Name"] = "Chris Harris";
dr["Age"] = "40";
dr["Occupation"] = "Doctor";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["Name"] = "Sean Williams";
dr["Age"] = "39";
dr["Occupation"] = "Plumber";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["Name"] = "Paul Newcombe";
dr["Age"] = "38";
dr["Occupation"] = "Lecturer";
dt.Rows.Add(dr);
GridView1.DataSource = dt;
GridView1.DataBind();
Now comes the slightly tricky part. In order to populate the GridView
's DropDownList
, we need to run a loop through every row and find the DropDownList
within that row. This could be avoided if we have a static DataSource
. Here though, we are using a DataSource
generated at runtime.
We find the DropDownList
and then bind it to the same dynamic DataSource's Occupation
column.
foreach (GridViewRow grdRow in GridView1.Rows)
{
DropDownList drdList =
(DropDownList)(GridView1.Rows[grdRow.RowIndex].Cells[1].FindControl(
"DropDownList1"));
drdList.DataSource = dt;
drdList.DataValueField = "Occupation";
drdList.DataTextField = "Occupation";
drdList.DataBind();
}
That is it. We now have a DropDownList
in the GridView
. We can use similar code with minor enhancements and an EditTemplate
to display
a DropDownList
in edit mode while allowing the user to see a label in normal mode.
Get individual DataKey values for multiple DataKeys
If you have a GridView
with multiple data key names like DataKeyNames="targetid,RequestID"
and now you want to get their individual values,
you have to do the following:
string targetID= gv.DataKeys[e.RowIndex].Values[0].ToString();
string requestID= gv.DataKeys[e.RowIndex].Values[1].ToString();
Also, you can use:
gv.DataKeys[e.SelectedIndex].Values["targetid"];
gv.DataKeys[e.SelectedIndex].Values["RequestID"];
This question has been asked many times, and recently over here.
Showing a DropDownList in a GridView using an EditItemTemplate
A very common question on forums is about showing a dropdown in a GridView
when the GridView
is being edited.
This is fairly easy to do using an EditItemTemplate
.
The UI is fairly simple. A GridView
is shown with an Edit command in the first column and then three columns (Name, Age, and Occupation) that would be holding the data.
Once the user clicks on the Edit button, a GridView
is shown in the Occupation column. Here is a snapshot of what the screen looks like when the grid is in Edit mode.
Here is the UI code. Note the use of the Edit CommandField
and EditItemTemplate
. A normal ItemTemplate
is used for the scenario
when the GridView
is not in Edit mode.
<columns>
<asp:commandfield showeditbutton="True">
<asp:templatefield headertext="Name">
<itemtemplate>
<%# DataBinder.Eval(Container.DataItem, "Name")>
</itemtemplate>
</asp:templatefield>
<asp:templatefield headertext="Age">
<itemtemplate>
<%# DataBinder.Eval(Container.DataItem, "Age")>
</itemtemplate>
</asp:templatefield>
<asp:templatefield sortexpression="Occupation" headertext="Occupation">
<edititemtemplate>
<asp:dropdownlist id=""DropDownList1" runat="server">
</edititemtemplate>
<itemtemplate>
<%# DataBinder.Eval(Container.DataItem,"Occupation")>
</itemtemplate>
</asp:templatefield>
</columns>
Let's move to the code-behind. Two important points here:
The first one is that the binding to the DropDownList
must be done in the RowDataBound
event and not the RowEditing
event
of the GridView
. The RowEditingEvent
does not understand e.Row
. Also, the if
condition in the RowDataBound
method
is important, especially the check for the DataControlRowState
edit. This is because RowDataBound
is fired every time the GridView
is bound
and we only want to handle our scenario in the case when it is being edited.
The second important point (although not directly related to this sample) is the use of the RowEditing
event. This is what displays the DropDownList
when the GridView
enters Edit
mode.
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow &&
(e.Row.RowState & DataControlRowState.Edit) == DataControlRowState.Edit)
{
DropDownList dl = (DropDownList)e.Row.FindControl("DropDownList1");
dl.DataSource = dt;
dl.DataValueField = "Occupation";
dl.DataTextField = "Occupation";
dl.DataBind();
}
}
protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
GridView1.EditIndex = e.NewEditIndex;
GridView1.DataBind();
}
There you go. That is all there is to displaying a DropDownList
in Edit
mode in a GridView
.
Displaying an image in a GridView
A frequent question on forums is about displaying an image in a GridView
. An ImageField
is enough to do this. Here is what
a web page and its code would look like (for a very simple scenario).
All we need to do is bind to the DataImageUrlField
field. As a note, an Images folder (containing the images) has been included in the project.
This will be hosted on the server, along with the page itself.
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("PictureUrl");
DataRow dr = dt.NewRow();
dr["Name"] = "Hydrangeas.jpg";
dr["PictureUrl"] = ResolveUrl("~/Images/Hydrangeas.jpg");
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["Name"] = "Lighthouse.jpg";
dr["PictureUrl"] = ResolveUrl("~/Images/Lighthouse.jpg");
dt.Rows.Add(dr);
GridView1.DataSource = dt;
GridView1.DataBind();
The code behind is also fairly self explanatory. A DataTable
acts as the source to the GridView
. Note the use of the ResolveUrl
method
to resolve the path of the image. That is all there is to get an image to display in a GridView
.
Displaying a running total inside a GridView
Running a total for a numeric field inside a GridView
is a topic that is often discussed. This can be easily achieved by using a footer template. Take a look at the UI and code below.
Adding ShowFooter = "true"
is a necessity to show footers in a GridView
. The FooterTemplate
takes care of the rest.
The code-behind makes use of the RowDataBound
method to do a sum.
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
sum = sum + Convert.ToInt32(DataBinder.Eval(e.Row.DataItem, "Salary"));
}
if (e.Row.RowType == DataControlRowType.Footer)
{
Label obj = (Label)e.Row.FindControl("Sum");
obj.Text = Convert.ToString(sum);
}
}
The sum is calculated in this method and then finally assigned to the label. That is all there is to displaying the sum in a footer.
Displaying a nested GridView
Displaying nested GridView
is a fairly common requirement. It is fairly simple to do using an ItemTemplate
.
The UI and code is shown below. Not much to explain there - a couple of text fields and two grids (one inside the other).
This code is put into the Page_Load
event.
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Age");
DataRow dr = dt.NewRow();
dr["Name"] = "Chris Harris";
dr["Age"] = "40";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["Name"] = "Sean Williams";
dr["Age"] = "39";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["Name"] = "Paul Newcombe";
dr["Age"] = "38";
dt.Rows.Add(dr);
GridView1.DataSource = dt;
GridView1.DataBind();
foreach (GridViewRow grdRow in GridView1.Rows)
{
GridView nestedView = (GridView)(
GridView1.Rows[grdRow.RowIndex].Cells[2].FindControl("GridView2"));
nestedView.DataSource = dt;
nestedView.DataSource = LoadNestedData(grdRow.RowIndex);
nestedView.DataBind();
}
LoadNestedData
is the method that loads the nested grid. The code is not displayed here, but the code comments should give a general idea of what this does.
The DataSource
to the nested GridView
is set at runtime. Fairly simple.
History
- Added 06/09/2011 : Get individual
DataKey
value for multiple DataKey
s. - Added 06/09/2011 : Show a
DropDownList
in a GridView
in edit mode. - Added 10/09/2011 : Displaying an image in a
GridView
. - Added 12/09/2011 : Importing an Excel file / Displaying a running total.
- Added 14/09/2011 : Displaying a nested
GridView
.
We are continuously looking for feedback from readers. If there is any other topic a reader wants us to include, please feel free to post about it in the comments section below.
We will try to continue to update this article with more frequent questions from the Q&A and other forums.