Shows how to make your GridViews row-selectable and how to bind to a row's detail.
This is pretty basic stuff, and this feature and others are already built in to many third party controls. But, you can also do it very easily with the ASP.NET GridView control that comes with Visual Studio. Here's how.
First, the key "trick" to learn is to be able to add an onclick javascript event to each row in the grid that will fire the intrinsic ASP.NET __doPostBack() method. This is easily accomplished in the RowDataBound event handler, like this:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.Attributes.Add("onclick",
Page.ClientScript.GetPostBackEventReference(sender as GridView,
"Select$" + e.Row.RowIndex.ToString()));
}
}
What the above code does is to add the "onclick" event to each table row (<TR>) in the Grid using the GetPostBackEventReference method built into ASP.NET. The "Select$" is used because ASP.NET uses the $ as its control hierachy delimiter. What will result in the page is something like this, where the 'Select$2' represents the second row of the Grid:
<tr onclick="__doPostBack('ctl00$MainContent$GridView1','Select$2')"><td>blah blah/td><td>blah, blah</td></tr>
The __doPostBack function takes two arguments, eventTarget and eventArgument. The eventTarget contains the ID of the control that causes the postback and the eventArgument contains any additional data associated with the control.
Note that the two hidden fields, “__EVENTTARGET” and “__EVENTARGUMENT,” are automatically declared by ASP.NET. The value of the eventTarget and eventArgument are stored in these hidden fields.
Also, the two hidden variables can be accessed from server-side codebehind using the forms/params collection. In this case the method provides us with the GridViewRowEventArgs class, which contains the selected Row.RowIndex property.
The actual Javascript __doPostBack function looks like this:
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
At this point all we need to do is use that RowIndex as the indexer into our DataSource to get the actual DataRow (or other bindable object) the user selected. The rest is simple:
protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
GridViewRow row = this.GridView1.SelectedRow;
DataTable dt = (DataTable) Cache["dt"];
int y = row.DataItemIndex;
var q = dt.Rows[y];
List<DataRow> rowList = new List<DataRow>();
rowList.Add(q);
// we need to clear out the grid to avoid attributes blowing up on new selected row
GridView1.DataSource = null;
FormView1.DataSource = rowList;
FormView1.DataBind();
// OK to rebind the grid now...
BindData();
}
You can bind the selected row data to a FormView or any other suitable control, or even use it for custom logic.
There are a couple of necessary tricks to make all this work properly:
1. You must have EnableEventValidation="false" in your @Page declaration. Otherwise, the runtime will attempt to validate the generated event items and it will fail, because you added them "on the fly client-side".
2. You may have to temporarily set your GridView's DataSource to null before binding the secondary control, and then rebind it afterward. Otherwise you run the risk of getting your onclick attributes discombobulated by the all-knowing ASP.NET runtime, which blindly does whatever it wants without any regard to your little adventures.
In the sample Visual Studio 2010 project, I simply bind one of our eggheadcafe.com RSS Feeds to a DataSet as a convenient way to get some data:
private void BindData()
{
DataSet ds = new DataSet();
XmlReader rdr = XmlReader.Create("http://www.eggheadcafe.com/rss.xml");
ds.ReadXml(rdr, XmlReadMode.InferSchema);
if (Cache["dt"] == null)
Cache["dt"] = ds.Tables[2];
GridView1.DataSource = (DataTable)Cache["dt"];
GridView1.DataBind();
}
First, the key "trick" to learn is to be able to add an onclick javascript event to each row in the grid that will fire the intrinsic ASP.NET __doPostBack() method. This is easily accomplished in the RowDataBound event handler, like this:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.Attributes.Add("onclick",
Page.ClientScript.GetPostBackEventReference(sender as GridView,
"Select$" + e.Row.RowIndex.ToString()));
}
}
What the above code does is to add the "onclick" event to each table row (<TR>) in the Grid using the GetPostBackEventReference method built into ASP.NET. The "Select$" is used because ASP.NET uses the $ as its control hierachy delimiter. What will result in the page is something like this, where the 'Select$2' represents the second row of the Grid:
<tr onclick="__doPostBack('ctl00$MainContent$GridView1','Select$2')"><td>blah blah/td><td>blah, blah</td></tr>
The __doPostBack function takes two arguments, eventTarget and eventArgument. The eventTarget contains the ID of the control that causes the postback and the eventArgument contains any additional data associated with the control.
Note that the two hidden fields, “__EVENTTARGET” and “__EVENTARGUMENT,” are automatically declared by ASP.NET. The value of the eventTarget and eventArgument are stored in these hidden fields.
Also, the two hidden variables can be accessed from server-side codebehind using the forms/params collection. In this case the method provides us with the GridViewRowEventArgs class, which contains the selected Row.RowIndex property.
The actual Javascript __doPostBack function looks like this:
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
At this point all we need to do is use that RowIndex as the indexer into our DataSource to get the actual DataRow (or other bindable object) the user selected. The rest is simple:
protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
GridViewRow row = this.GridView1.SelectedRow;
DataTable dt = (DataTable) Cache["dt"];
int y = row.DataItemIndex;
var q = dt.Rows[y];
List<DataRow> rowList = new List<DataRow>();
rowList.Add(q);
// we need to clear out the grid to avoid attributes blowing up on new selected row
GridView1.DataSource = null;
FormView1.DataSource = rowList;
FormView1.DataBind();
// OK to rebind the grid now...
BindData();
}
You can bind the selected row data to a FormView or any other suitable control, or even use it for custom logic.
There are a couple of necessary tricks to make all this work properly:
1. You must have EnableEventValidation="false" in your @Page declaration. Otherwise, the runtime will attempt to validate the generated event items and it will fail, because you added them "on the fly client-side".
2. You may have to temporarily set your GridView's DataSource to null before binding the secondary control, and then rebind it afterward. Otherwise you run the risk of getting your onclick attributes discombobulated by the all-knowing ASP.NET runtime, which blindly does whatever it wants without any regard to your little adventures.
In the sample Visual Studio 2010 project, I simply bind one of our eggheadcafe.com RSS Feeds to a DataSet as a convenient way to get some data:
private void BindData()
{
DataSet ds = new DataSet();
XmlReader rdr = XmlReader.Create("http://www.eggheadcafe.com/rss.xml");
ds.ReadXml(rdr, XmlReadMode.InferSchema);
if (Cache["dt"] == null)
Cache["dt"] = ds.Tables[2];
GridView1.DataSource = (DataTable)Cache["dt"];
GridView1.DataBind();
}
No comments:
Post a Comment