Click here to Skip to main content
6,595,854 members and growing! (18,852 online)
Email Password   helpLost your password?
Web Development » ASP.NET » General     Intermediate License: The Code Project Open License (CPOL)

Some Tips and Tricks for using an ObjectDataSource with a Gridview

By kubben

This article uses a generic collection as the data source for the Gridview. Some code is requried to get the sorting to work. There is an examples of using the pager template along with a simple Gridview printer.
Windows, .NET, ASP.NET, Visual Studio, Dev
Posted:4 Jul 2006
Views:172,966
Bookmarked:81 times
Unedited contribution
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
38 votes for this article.
Popularity: 6.74 Rating: 4.27 out of 5
3 votes, 7.9%
1
2 votes, 5.3%
2
3 votes, 7.9%
3
3 votes, 7.9%
4
27 votes, 71.1%
5

Sample Image - GridViewObjectDataSource.gif

Introduction

This 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.

Background

I 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 Code

First 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.

Class diagram of People class

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.  This generic collection has one drawback.  The sorting doesn�t work automatically, like it does if you return a Dataset, DataTable or DataView from an ObjectDataSource.  If you try sorting you will actually get this exception:

The data source 'ObjectDataSource1' does not support sorting with IEnumerable data. Automatic sorting is only supported with DataView, DataTable, and DataSet.

So the way I got around this exception is I created an event handler for the Gridview.sorting event.  In this event I set the ObjectDataSource SortExpression and SortDirection parameters to the values I get from the Gridview from the sorting event.  Finally I cancel the sorting event so I don�t get the exception.  Here is the event code:

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 GetData(string p_sortExpression, string p_sortDirection)
{
    List peoples = new List();
    //Normally here you would access your database to populate this object.

    //Since I want this example not to have any need to connect to a database

    //I am just creating the data.

    //Note when caching the objectdatasource a chance in parameters being

    //passed in will case the select method to be called again.

    #region Creating data
    peoples.Add(new People(1, "Bart", "Long", "Mower", 10.00M, 18));
    ...
    peoples.Add(new People(21, "Gary", "Black", "Doctor", 46.50M, 29)); 
    #endregion

    //We sort the generic list if requested too

    if (p_sortExpression != null && p_sortExpression != string.Empty)
    {
        peoples.Sort(new PeopleComparer(p_sortExpression));
    }

    //Now that we have sorted check to see if the sort direction is desc

    if (p_sortDirection != null && p_sortDirection == "Desc")
    {
        peoples.Reverse();
    }

    return peoples;
}

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.

Another tip for storing the row key in the Gridview, but not displaying it.  I create a template column and set its visible property = false.  Then in the item and edit template I have a label that is bound to the key name.  This way I can access the key later on.

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 DataBinder.Eval(Container.DataItem, "Key") verses the newer Bind("Key") For some reason when you pass the columns from your Gridview to the Printing page it won't bind right to the Bind("Key") syntax.

Finally I just added this in since I thought It was a good thing to know.  If you want to search a generic collection and find a certain object in the collection based off a property in the object here is one way of doing it.  In this example you pass in the key value and get back a People object if it can be found in the generic collection.

private People FindByID(int p_key)
{
    List peoples = (List)this.ObjectDataSource1.Select();
    return peoples.Find(delegate(People p) {return p.Key == p_key;});
        
}

In this code you see an example of an anonymous method delegate.

Conclusion

Well, 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

kubben


Member
I started my programmer career over 13 years ago doing COBOL and SAS on a MVS mainframe. It didn't take long for me to move into windows programming. I started my windows programming in Delphi (Pascal) with a Microsoft SQL server back end. I started working with vb.net when the beta 2 came out in 2001. After spending most of my programming life as a windows programmer I started to check out asp.net in 2004. I achieved my MCSD.net in April 2005. I have done a lot of MS SQL database stuff. I have a lot of experience with Window Service and Web services as well. For three years now I programmed in C# programming as a consultant. I really enjoy it and found the switch between vb.net and C# to be mostly syntax. I am now back to programming in vb.net in my current position.

On a personal note I am a born again Christian, if anyone has any questions about what it means to have a right relationship with God or if you have questions about who Jesus Christ is, send me an e-mail. ben.kubicek[at]netzero[dot]com You need to replace the [at] with @ and [dot] with . for the email to work. My relationship with God gives purpose and meaning to my life.
Occupation: Web Developer
Location: United States United States

Other popular ASP.NET articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 127 (Total in Forum: 127) (Refresh)FirstPrevNext
GeneralPaging and Sorting Events Pinmemberfellippe0:42 1 Apr '09  
GeneralRe: Paging and Sorting Events Pinmemberkubben1:21 1 Apr '09  
GeneralBi-directional sorting doesn't work Pinmembercount_rugen10:30 25 Feb '08  
GeneralRe: Bi-directional sorting doesn't work Pinmemberkubben10:51 25 Feb '08  
GeneralRe: Bi-directional sorting doesn't work Pinmembercount_rugen11:11 25 Feb '08  
GeneralRe: Bi-directional sorting doesn't work Pinmemberkubben11:21 25 Feb '08  
GeneralRe: Bi-directional sorting doesn't work Pinmembercount_rugen11:34 25 Feb '08  
GeneralRe: Bi-directional sorting doesn't work Pinmemberkubben16:59 25 Feb '08  
GeneralRe: Bi-directional sorting doesn't work Pinmembermr_gold7:50 10 May '08  
GeneralRe: Bi-directional sorting doesn't work Pinmemberkubben9:17 10 May '08  
GeneralRe: Bi-directional sorting doesn't work PinmemberMember 382185911:22 21 Aug '08  
QuestionData source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberdsmportal4:07 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberkubben5:18 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberdsmportal6:01 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberkubben6:35 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberdsmportal7:00 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberkubben10:28 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberdsmportal11:47 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberkubben12:48 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberdsmportal13:34 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberkubben14:39 12 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberdsmportal5:17 13 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberkubben11:05 13 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberdsmportal11:57 13 Jan '08  
GeneralRe: Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource. Pinmemberkubben13:45 13 Jan '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 4 Jul 2006
Editor:
Copyright 2006 by kubben
Everything else Copyright © CodeProject, 1999-2009
Web22 | Advertise on the Code Project