asp.netviewstatepage-lifecycledatapager

Programmatically Assigning PagedControlID to DataPAger


So I am struggling with, what I believe is, more of a Page Lifecycle/ViewState issue. I have a page which hosts three ListView controls. Only one will be visible at any time. I want to use a single DataPager, programmatically assigning the PagedControlID of the DataPager to the ID of the visible ListView. It may be relevant to point out that the DataPager is included in user-defined user control (ASCX) which is primarily just a pass through to the DataPager.

The error seems straightforward enough ...

[InvalidOperationException: No IPageableItemContainer was found. Verify that either the DataPager is inside an IPageableItemContainer or PagedControlID is set to the control ID of an IPageableItemContainer.]
System.Web.UI.WebControls.DataPager.LoadControlState(Object savedState) +628838
   System.Web.UI.Control.LoadControlStateInternal(Object savedStateObj) +137
   System.Web.UI.Page.LoadAllState() +342
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2533

Here is the current sequence of events.

  1. I have a "SUMMARY" page which links to the "Detail" page where the ListView(s) are displayed.

  2. When I click on the link in the "SUMMARY" page, control is TRANSFERRED (not redirected) to the "DETAIL" page allowing me to pass the properties of the "SUMMARY" page forward via the Page.PreviousPage property to the "DETAIL" page.

  3. In the OnInitComplete event of the target "DETAIL" page I assign the property values from the "PreviousPage" to properties in the new "DETAIL" page.

    protected override void OnInitComplete(EventArgs e)
    {
        IExceptionPage previousPage = (PreviousPage != null) ? ((IExceptionPage)PreviousPage) : null;
        if (previousPage != null)
        {
            this.ActiveListID       = previousPage.ActiveListID;
            this.ActionType         = previousPage.ActionType;
            this.CategoryTheme      = previousPage.CategoryTheme;
            this.ExceptionCode      = previousPage.ExceptionCode;
        }
    }
    
  4. These “DETAIL” page properties simply read and write to ViewState, so by assigning values to these properties, in the OnInitComplete event, I am expecting that these values will be persisted between future Postbacks on this page. One of these properties is the ID of the ListView I want the DataPager to bind to.

    public String ActiveListID
    {
        get { return (String)ViewState["ActiveListID"]; }
        set { ViewState["ActiveListID"] = value; }
    }
    
  5. Now in the OnPreLoad event I set the PagedControlID property of the DataPager control to the ID of the ListView stored in the ViewState attribute.

  6. The initial load of the page proceeds without a problem.

  7. The problem occurs when I attempt to do anything that posts back to the page. Between the OnInitComplete and the OnPreLoad event handlers the "InvalidOperationException" error is thrown.

Looking at the error it appears that the PagedControlID property of the Pager is not being properly set. What I do not understand is why.

  1. Shouldn't the pager remember the PagedControlID set previously? Isn't it stored in the Pagers own ViewState?

  2. If not, then why is it throwing an error BEFORE OnPreLoad, where the ViewState tracking is turned on (which is inconsistent with Microsoft’s MSDN Example where they set it way down in the OnLoad event)?

  3. I have attempted to set the PagedControlID BEFORE the PreLoad event, in the OnInit event, but I run into the problem that the ID I set when the page initially loaded is no longer present in ViewState and as a result I get a NULL value back. I also, do not understand why the ID is not being saved to ViewState. According to the MSDN documentation I am referencing, setting it in the OnInitComplete event should include it in the tracked ViewState values.

If anybody has any thoughts on this I would be most appreciative.

Thanks, G


Solution

  • Based on the stack trace, the InvalidOperationException is thrown during LoadControlState, which is called before view state has been loaded. If you don't set PagedControlID declaratively (in the .ascx), then it will still be an empty string at this point in the page life cycle.

    The MSDN Library example differs from your code in one important respect: the DataPager is being dynamically constructed, initialized, and added to the page. LoadControlState doesn't get called until the DataPager is added to the page, at which point PagedControlID has already been initialized.

    It doesn't appear that the ASP.NET team really intended to support the PagedControlID property being set dynamically. I suppose the only thing you can try is to emulate the MSDN Library example and add the DataPager dynamically.