|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionThis article attempts to show a few tricks with the new Gridview that we have in asp.net 2.0. This article addresses using the ObjectDataSource to populate the Gridview. In this example the ObjectDataSource returns a generic collection of an object. There is some special coding that needs to happen to get the sorting of colums to work. I also show how to use the pager template to do custom navigation. I have also included a simple Gridview printing example. BackgroundI have been meaning to write an article on the Gridview for sometime. I really like the improvements .net 2.0 has over the Datagrid. Using a Gridview in combination with a SqlDataSource or ObjectDataSource will give you a lot of functionality without having to write a lot of code. The CodeFirst let’s take a look at the People class I put together. Notice I just have a few properties on it. I just needed something simple for this example so this is what I came up with. Next let’s talk about ObjectDataSource. Both the SqlDataSource and the ObjectDataSource give you a lot of functionality when you point your Gridview to one of these objects for data. I have only implemented the Select method for this example, but these object also allow you to implement Insert Update and Delete. With a SqlDataSource you point the method to a database stored procedure, table or query. With an ObjectDataSource, which is what is in this example, you point the Select method to a type and a method name. Notice that these object can implement caching. Here’s the HTML: <asp:ObjectDataSource id=ObjectDataSource1 CacheExpirationPolicy="Sliding"
CacheDuration="300" TypeName="DataAccess"
SelectMethod="GetData" runat="server">
<SELECTPARAMETERS>
<asp:Parameter Direction="input" Type="string" Name="p_sortExpression">
</asp:Parameter>
<asp:Parameter Direction="input" Type="string" Name="p_sortDirection">
</asp:Parameter>
</SELECTPARAMETERS>
</asp:ObjectDataSource>
Once the ObjectDataSource is set up you just need to point the Gridview DataSourceID equal to the ObjectDataSource name. I am going to have my ObjectDataSource method (GetData) return a List protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
//Since we are changing the parameters we will not use the cached
//object datasource we will call the selecte method again.
if (ObjectDataSource1.SelectParameters[0].DefaultValue == null ||
ObjectDataSource1.SelectParameters[1].DefaultValue == null ||
ObjectDataSource1.SelectParameters[0].DefaultValue !=
e.SortExpression ||
ObjectDataSource1.SelectParameters[1].DefaultValue == "Desc")
{ //sort direction
ObjectDataSource1.SelectParameters[1].DefaultValue = "Asce";
}
else if (ObjectDataSource1.SelectParameters[1].DefaultValue == "Asce")
{
ObjectDataSource1.SelectParameters[1].DefaultValue = "Desc";
}
//Which column to sort on.
ObjectDataSource1.SelectParameters[0].DefaultValue = e.SortExpression;
//We have to do this or we will get an exception
e.Cancel = true;
}
The next thing I need is to implement the ObjectDataSource select method. It looks like this: public static List
Next we need to look at the PeopleComparer that is used to sort the generic collection. I use reflection so that I can just find the property that is passed into the comparer. Here is the code: public int Compare(People x, People y)
{
Type t = x.GetType();
PropertyInfo val = t.GetProperty(this.PropertyName);
if (val != null)
{
return Comparer.DefaultInvariant.Compare(val.GetValue(x,null),
val.GetValue(y,null));
}
else
{
throw new Exception(this.PropertyName +
" is not a valid property to sort on. It doesn't exist in the Class.");
}
}
Let’s move on to Navigation. I really like the pager functionality they have added to the Gridview. There are still a few things I would like my navigation to do. First is to have a label that tells me which page I am on out of how many pages. T he second is to have the first and last button disappear when you are on the first and last pages respectively. Finally, it is also nice to have a dropdown that lets the user choose how many items per page are shown. The thing to note about doing your own navigation in a pager template is that you just need to mark the controls with the correct CommandName which should be “Page” and the correct CommandArgument which will be “First”, “Prev”, “Next”, “Last”. If you implement these two things paging will work as long as you have a SqlDataSource or a ObjectDataSource behind the scenes. This is where it is nice to implement some sort of Caching on these objects so you don’t hit your select method every time you navigate your Gridview. Here’s the HTML for the Pager Template: <PagerTemplate>
<table id="tbPager" width="100%">
<tr>
<td>Page <asp:Label ID="lbCurrentPage" runat="server"></asp:Label> of
<asp:Label ID="lbTotalPages" runat="server"></asp:Label>
</td>
<td>
<asp:DropDownList ID="ddlPageItems" runat="server" AutoPostBack="True"
OnSelectedIndexChanged="ddlPageItems_SelectedIndexChanged">
<asp:ListItem>5</asp:ListItem>
<asp:ListItem Selected="True">10</asp:ListItem>
<asp:ListItem>20</asp:ListItem>
</asp:DropDownList></td>
<td align="right">
<asp:LinkButton ID="lbtnFirst" runat="server" CommandName="Page"
CommandArgument="First" Text="<<"></asp:LinkButton>
<asp:LinkButton ID="lbtnPrev" runat="server" CommandName="Page"
CommandArgument="Prev" Text="Prev"></asp:LinkButton>
<asp:LinkButton ID="lbtnNext" runat="server" CommandName="Page"
CommandArgument="Next" Text="Next"></asp:LinkButton>
<asp:LinkButton ID="lbtnLast" runat="server" CommandName="Page"
CommandArgument ="Last" Text=">>"></asp:LinkButton>
</td>
</tr>
</table>
</PagerTemplate>
Next I write a handler for the Gridview.PreRender event. This is where I show or hide the first and last navigation buttons and where I set the current page and total pages. One thing to note about the pager template. Since the pager template can be both a top and bottom pager control you have to check both if you are using them. You will find the control resolves to TopPagerRow and BottomPagerRow off of the Gridview control. In this example I only use the TopPagerRow. I have found that when using both top and bottom some of the control events don’t work properly. Like in the case of the dropdownlist event for the number of items on a page. It seems to work the first time on the BottomPagerRow, but doesn’t fire after that. Anyway, here is the pre-render event for the Gridview. protected void GridView1_PreRender(object sender, EventArgs e)
{
//This logic displays the Page # of # in the pager template
if (GridView1.TopPagerRow != null)
{
((Label)GridView1.TopPagerRow.FindControl("lbCurrentPage")).Text =
(GridView1.PageIndex + 1).ToString();
((Label)GridView1.TopPagerRow.FindControl("lbTotalPages")).Text =
GridView1.PageCount.ToString();
//This makes the first and last button disappear when on the
//first and last pages.
((LinkButton)GridView1.TopPagerRow.FindControl("lbtnFirst")).Visible =
GridView1.PageIndex != 0;
((LinkButton)GridView1.TopPagerRow.FindControl("lbtnLast")).Visible =
GridView1.PageCount != (GridView1.PageIndex +1);
DropDownList ddlist =
(DropDownList)GridView1.TopPagerRow.FindControl("ddlPageItems");
ddlist.SelectedIndex =
ddlist.Items.IndexOf(ddlist.Items.FindByValue(ViewState["DropDownPageItems"].ToString()));
}
}
I also have an example of simple Gridview printing. The idea is to put the columns of the Gridview you want to print into a session variable. Then in another session variable you put the datasource. So in the print Gridview page you just have to add the columns and set the datasource. Here is the print button method: protected void btnPrint_Click(object sender, EventArgs e)
{
Session["PrintGridviewColumns"] = this.GridView1.Columns;
Session["PrintGridViewDataSource"] = this.ObjectDataSource1.Select();
//Do some java script to bring up the Printing form in a different
//browser window
StringBuilder sb = new StringBuilder();
sb.Append("<script>");
sb.Append(Environment.NewLine);
sb.Append("window.open(\"PrintGridview.aspx\",\"Print\",\"top=5,left=5\");");
sb.Append(Environment.NewLine);
sb.Append("</script>");
ClientScript.RegisterStartupScript(this.GetType(), "print", sb.ToString());
}
Here is the page load for the PrintGridView.aspx: protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack && Session["PrintGridviewColumns"] != null &&
Session["PrintGridViewDataSource"] != null)
{
//Add the columns to the print gridview
foreach (DataControlField dcf in (DataControlFieldCollection)Session["PrintGridviewColumns"])
{
this.GridView1.Columns.Add(dcf);
}
//Set the data source and databind
this.GridView1.DataSource = Session["PrintGridViewDataSource"];
this.GridView1.DataBind();
//Do some java script to bring up the printer dialog
StringBuilder sb = new StringBuilder();
sb.Append("<script language="javascript">");
sb.Append("window.print();");
sb.Append("</script>");
ClientScript.RegisterStartupScript(this.GetType(),"print", sb.ToString());
Session["PrintGridviewColumns"] = null;
Session["PrintGridViewDataSource"] = null;
}
}
Another tip has to do with the DataFormatString in a bound column. In my example, the DollarsPerHour column is formatted to have a dollar sign and decimal point. The tip here is that for that column you must set HTMLEncode = false. Otherwise, the formatting doesn't work. foreach (GridViewRow tmpGVR in p_gridview.Rows)
{
tmpLBKey = (Label)tmpGVR.FindControl("lbKey");
} //foreach
One last thing on the Gridview printing example. If you have template columns you must use the private People FindByID(int p_key)
{
List
In this code you see an example of an anonymous method delegate. ConclusionWell, I hope you have learned some new things about the Gridview and some tips to help you make your implementation of the Gridview work in whatever your situation calls for. | ||||||||||||||||||||