Tuesday, January 15, 2008

Gridview paging within other data control

One approach to solve a programming problem is to re-create the problem with minimal amount of code and work on it. It has 2 effects on solving the problem. One is that it lets you focus on the problem without any distraction from other parts of the code which could be huge. The other one is that it shorten the code change and debug/test loop. You can almost get instantaneous feed back from your code changes.

Here is how I apply the principle. In my work project, I encounter a problem to paginate a GridView which is inside a data control. I am keep getting error message saying ""Object reference not set to an instance of an object.".

Reproduce the problem
So I start out to create the problem with minimal amount of code. The following code shows a repeater control and within the repeater control it display a GridView control which has pagination enabled. Upon page loading, it sets the datasource for the repeater and call the DataBind() method to update the repeater and its child controls. It works fine on the first initial page load, but fails when you click on the numbers that represents each page of the GridView.


   <asp :Repeater ID="Repeater1" runat="server">
      
<ItemTemplate>
          
< asp:GridView ID="GridView1" runat ="server" AllowPaging="True" EnableSortingAndPagingCallbacks ="True"
              
PageSize="2" DataSource=' <%#  ( (A) Container.DataItem).b %> '>
          
</asp:GridView >
      
</ItemTemplate>
   
</ asp:Repeater>

    protected void Page_Load(object sender, EventArgs  e)
    {
        if (!IsPostBack)
        {
             A a = new A();
            a.b = new  int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
 
            Repeater1.DataSource = new  List<A>(new A[] {a});
            Repeater1.DataBind ();
        }
    }
It is good sign that I am able to reproduce the problem with the minimal amount of code shown above without any Ajax, JavaScript, database access.

Tackle the problem
A quick search on the e-books that I have and on the Internet could not find any information on the topic. One related the article shows a technique to display GridView within a GridView is the closest that I can find. It uses 2 datasource controls and bind the child datasource at runtime. In the problem code above, I bind the datasource declaratively. Maybe declaration of child datasource only takes effect via the page loading not the subsequent page post-back. That is exactly how my test code fails.

So I quickly change the code as the following: (changes are in bold)

   < asp:Repeater ID="Repeater1" runat ="server" OnItemDataBound ="Repeater1_ItemDataBound">
      
<ItemTemplate>
          
<asp:GridView ID="GridView1" runat="server" AllowPaging ="True" EnableSortingAndPagingCallbacks="True"
              
PageSize="2"
OnDataBinding="GridView1_DataBinding" >
          
</asp:GridView>
      
</ItemTemplate>
   
</asp:Repeater>


    protected void Repeater1_ItemDataBound(object sender,  RepeaterItemEventArgs e)
    {
        GridView gv = ( GridView) e.Item. FindControl("GridView1");
        gv.DataBind();
    }
 
    protected void GridView1_DataBinding(object sender,  EventArgs e)
    {
        GridView gv = ( GridView)sender;
        gv.DataSource = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
     }

Basically, I removed the declarative databinding for the GridView and intercept 2 events that will allow me to populate the GridView programmatically. The chain of events that fires are Repeater1_Databinding, Repeater1_itemDataBound, Gridview1_DataBinding. The last 2 events will repeat as many times as the amount of data in the repeater.

Now we have a solution and just need to port it to my work project.





No comments: