ListBox inside UserControl ViewState lost

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • kucheravy
    New Member
    • Jul 2009
    • 3

    ListBox inside UserControl ViewState lost

    Hi, everybody. I have this problem. When I put a <asp:ListBox on a web page and populate the data in the page Page_Load event the ViewState for the control is saved and loaded (after postback) correctly. In other words the Items property of the ListBox is populated from the ViewState after the postback. However if I put this ListBox inside a UserControl, expose the Items list, and populate it with the data in the same manner (in the page Page_Load event), the data is lost after the postback. What is wrong?

    Here is the code with viewstate working:

    Page:
    Code:
    <html xmlns="http://www.w3.org/1999/xhtml">
    <body>
    <form id="MainForm" runat="server">
       <asp:ListBox ID="ctrlDynList" runat="server" />
       <br />
       <asp:Button ID="btnSubmit" Text="Submit" onclick="btnSubmit_Click" runat="server" />
    </form>
    </body>
    </html>
    Code behind:
    ...
    Code:
       public partial class MultiSelect : Page {
          protected void Page_Load(object sender, EventArgs e) {
             if(!IsPostBack) {
                ctrlDynList.Items.Add(new ListItem("One", "1"));
                ctrlDynList.Items.Add(new ListItem("Two", "2"));
             }
          }
    ...
    }
    Here is the code with viewstate not working:

    Page:
    Code:
    <%@ Register Src="~/Test/SimplePanel.ascx" TagPrefix="uc" TagName="SimplePanel" %>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <body>
    <form id="MainForm" runat="server">
       <uc:SimplePanel ID="ctrlDynList" runat="server" />
       <br />
       <asp:Button ID="btnSubmit" Text="Submit" onclick="btnSubmit_Click" runat="server" />
    </form>
    </body>
    </html>
    Code behind:
    ...
    Code:
       public partial class MultiSelect : Page {
          protected void Page_Load(object sender, EventArgs e) {
             if(!IsPostBack) {
                ctrlDynList.Items.Add(new ListItem("One", "1"));
                ctrlDynList.Items.Add(new ListItem("Two", "2"));
             }
             else {
    // The ctrlDynList.Items is empty in this case.
    // It is not loaded from the viewstate like it does
    // in the case when the ListBox is placed on the page (not inside a user control)
             }
          }
    ...
    }
    User control:
    Code:
    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SimplePanel.ascx.cs" Inherits="MgScopes.MgScopesWeb.Test.SimplePanel" %>
    <asp:ListBox ID="ctrlListBox" runat="server" />
    Code behind:
    ...
    Code:
       public partial class SimplePanel : UserControlBase {
          protected void Page_Load(object sender, EventArgs e) {
          }
    
          public ListItemCollection Items {
             get { return ctrlListBox.Items; }
          }
       }
    ...
    }
    Last edited by Frinavale; Jul 24 '09, 02:51 PM. Reason: Please post code in [code] [/code] tags. Added code tags.
  • maliksleo
    New Member
    • Feb 2009
    • 115

    #2
    place your page_load event on the user control page not on main page and check your problem will be solved.

    maliksleo

    Comment

    • kucheravy
      New Member
      • Jul 2009
      • 3

      #3
      I tried all those kind of things. I finally found the answer I could never think on. In completely separate part of my code, not concerned to this sample, I called Controls.AddAt( Number, Control). This call blows ViewState for all controls in the collection and it seems like not only in this collection (I didn't investigate futher). It looks like the Framework keeps control's positions deep inside and if the positions change it cannot bind the ViewState to the controls. So, basically, if you care about the ViewState never call AddAt. Call Controls.Add instead. It works fine.

      Comment

      • Frinavale
        Recognized Expert Expert
        • Oct 2006
        • 9749

        #4
        It doesn't really matter how you fill the control you should not be experiencing this.
        Maliksleo's suggestion is good because it makes more sense to make the user control responsible for maintaining the list.

        I tested what you posted and could not reproduce your problem.

        This is what I have.

        The user control:
        Code:
        <%@ Control Language="vb" AutoEventWireup="false" CodeBehind="ctrl.ascx.vb" Inherits="ScratchPad.ctrl" %>
        <div>
            <asp:ListBox ID="theList" runat="server" AutoPostBack="true"></asp:ListBox>
            <asp:Label ID="selectedItem" runat="server"></asp:Label>
        </div>
        The code behind for the user control:
        Code:
        Public Partial Class ctrl
            Inherits System.Web.UI.UserControl
            Public ReadOnly Property Items() As ListItemCollection
                Get
                    Return theList.Items
                End Get
            End Property
        
            Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        
            End Sub
        
            Private Sub theList_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles theList.SelectedIndexChanged
                Dim selectedItemText As String = theList.SelectedItem.Text
                selectedItem.Text = selectedItemText
            End Sub
        End Class
        The aspx page:
        Code:
        <%@ Page Language="vb" AutoEventWireup="false" CodeBehind="WebForm3.aspx.vb" Inherits="ScratchPad.WebForm3" %>
        <%@ Register TagPrefix="uc" TagName="ListUserControl" Src="~/ctrl.ascx" %>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml">
        <head runat="server">
          <title></title>
        </head>
        <body>
            <form id="form1" runat="server">
            <uc:ListUserControl runat="server" ID="theListControl" />
            </form>
        </body>
        </html>
        The code behind for the aspx page:
        Code:
        Partial Public Class WebForm3
            Inherits System.Web.UI.Page
            Private _gridViewDataSource As DataView
            Private _detailsSource As DataSet
        
            Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
                If IsPostBack = False Then
                    theListControl.Items.Add(New ListItem("First Item", "1"))
                    theListControl.Items.Add(New ListItem("Second Item", "2"))
                End If
             End Sub
        End Class
        Like I said, this works perfectly fine.

        Comment

        • Frinavale
          Recognized Expert Expert
          • Oct 2006
          • 9749

          #5
          Originally posted by kucheravy
          I tried all those kind of things. I finally found the answer I could never think on. In completely separate part of my code, not concerned to this sample, I called Controls.AddAt( Number, Control). This call blows ViewState for all controls in the collection and it seems like not only in this collection (I didn't investigate futher). It looks like the Framework keeps control's positions deep inside and if the positions change it cannot bind the ViewState to the controls. So, basically, if you care about the ViewState never call AddAt. Call Controls.Add instead. It works fine.
          Oh! I see what the problem is.

          If you're calling the ControlCollecti on.AddAt() method then you're probably dynamically adding controls to your page. You did not mention this fact in your explanation of the problem...in fact your example code doesn't show you dynamically adding the control at all.

          The reason the ViewState isn't being remembered in your case is because of how the ASP Page Life Cycle works...

          This is what's happening:

          The web browser makes a request for the page.

          The Page Init Event occurs and all of the Objects required to do page processing are created.

          Right After the Page Init Event the ViewState of the controls are loaded.

          If your dynamically creating controls in the Page Load (or after that in the life cycle) then the ViewState for the control is not loaded because the Object doesn't exist!

          So, if you want the ViewState to load for dynamic controls, then you'll have to instantiate them (use the "new" statement) in the Page Init event. You also have to add them to their appropriate containers at this point too or you're going to experience a validation exception.

          Comment

          • kucheravy
            New Member
            • Jul 2009
            • 3

            #6
            I agree with your explanation and I new that. The problem is that as soon as you use Constrols.AddAt no controls that were already statically created and loaded into the Controls could Save/Load their state. It is not a problem of that one control that I add dynamically by AddAt. The whole collection of the controls loses the viewstate. BUT if you use Add everything is fine for the whole colleciton and that dynamically added control. I just wanted to state this problem for other people who might have the similar problem. I wasted a day figuring this out.

            Comment

            • Frinavale
              Recognized Expert Expert
              • Oct 2006
              • 9749

              #7
              Hmm I've never used the AddAt method before. I've always used the Add method.

              I guess it makes sense that it would mess up the static controls when you use the AddAt method though...becaus e something else (a static control) could be at that index.

              Thanks for added the info :)

              -Frinny

              Comment

              Working...