Custom Control Appears to Interfere with ViewState

C

Chris Newby

I have a very simple custom control that derives from WebControls.Panel and
implements INamingContainer.

It appear that controls created as children of my custom control are having
ViewState problems. For example, suppose I create a ListBox control as a
child of my custom control, attach a SelectedIndexChanged event handler to
it, and bind its data in the Page_Load event handler only if the current
request is not a post back. I load the page and initially everything is ok
and the list box has values. If I then select an item in the list box, the
page posts back to the server and thats when things get weird. The event
handler I orginally attached to SelectedIndexChanged doesn't get called and
upon further inspection the ListBox no longer has any items ... meaning they
were not loaded from ViewState.

If I take the ListBox in question *out* of my custom control, it works
perfectly fine. It also works if I put it inside a pure instance of
WebControls.Panel. So clearly something isn't right with my custom control.
I figure its something pretty obvious, but I'm new to custom controls ...
lucky me :)

Thought?

TIA//
 
W

Wilco Bauwer

When and how are you creating/adding the controls in your custom
control? Make sure the controls are created upon each request (which
includes postbacks). I suggest override CreateChildControls and
create/add them there. Also override the Controls property, do a call
to EnsureChildControls and then return the base.Controls collection.
 
C

Chris Newby

I have a custom control that is essentially implemented as follows:
================================================
[ ToolboxData("<{0}:TestPanel runat=server></{0}:TestPanel>") ]
[ ParseChildren( false ) ]
public class TestPanel : WebControl, INamingContainer
{
protected override void CreateChildControls()
{
this.Controls.AddAt( 0, new LiteralControl( "<span id=" + this.ID +
">" ) );
this.Controls.Add( new LiteralControl( "</span>" ) );
}
protected override void Render(HtmlTextWriter writer)
{
RenderChildren( writer );
}
}
================================================

In a given ASPX I have the following:
================================================
<cc1:TestPanel id=TestPanel1 runat="server">
<asp:ListBox id=ListBox1 runat="server" AutoPostBack="True">
</asp:ListBox>
</cc1:TestPanel>
================================================

The code behind for the ASPX has:
================================================
private void Page_Load(object sender, System.EventArgs e)
{
if( ! IsPostBack )
{
ListBox1.Items.Add( new ListItem( "1", "1" ) );
ListBox1.Items.Add( new ListItem( "2", "2" ) );
ListBox1.Items.Add( new ListItem( "3", "3" ) );
ListBox1.Items.Add( new ListItem( "4", "4" ) );
ListBox1.Items.Add( new ListItem( "5", "5" ) );
ListBox1.Items.Add( new ListItem( "6", "6" ) );
}
}

*Also assume that ListBox1 has its SelectedIndexChanged event wired to a
local handler.
================================================


When this ASPX initially loads, everything looks fine. However, if a browser
client then selectes an item in ListBox1 causing a post back, the ListBox
control's SelectedIndexChanged handler doesn't get called and none of the
orginal items are retrieved from ViewState. Inspecting the __VIEWSTATE field
sent to the client after initial page request and then the subsequent
postback showed that value goes from something relatively large to something
arbitrarily small (the digest/hash value of a blank ViewState I assume),
confirming to me that something is causing ViewState not work properly.

To compare, if you change the ASPX page to look like:
================================================
<asp:ListBox id=ListBox1 runat="server" AutoPostBack="True">
</asp:ListBox>
<cc1:TestPanel id=TestPanel1 runat="server">
</cc1:TestPanel>
================================================

.... removing the ListBox from the TestPanel container, things seem to work
fine; SelectedIndexChanged is called, the ListBox retains its items between
requests, and the __VIEWSTATE field doesn't seem to loose its value.
 
G

Guest

First, drop the literal controls, just use the enumerations on the HTMLWriter
class, easier and keeps the control tree smaller, being faster.

This is most likely related to the order in which things are being
persisted. You, as a user, are adding a control to a custom control list.
You need to maintain that order yourself. In the Controls property, override
the property to have the following:

public override ControlCollection Controls {
get {
EnsureChildControls();
return base.Controls;
}
}

This code causes the child controls that you created in CreateChildControls
to be added to the control tree becore other controls you added, preservig
the order.





Chris Newby said:
I have a custom control that is essentially implemented as follows:
================================================
[ ToolboxData("<{0}:TestPanel runat=server></{0}:TestPanel>") ]
[ ParseChildren( false ) ]
public class TestPanel : WebControl, INamingContainer
{
protected override void CreateChildControls()
{
this.Controls.AddAt( 0, new LiteralControl( "<span id=" + this.ID +
">" ) );
this.Controls.Add( new LiteralControl( "</span>" ) );
}
protected override void Render(HtmlTextWriter writer)
{
RenderChildren( writer );
}
}
================================================

In a given ASPX I have the following:
================================================
<cc1:TestPanel id=TestPanel1 runat="server">
<asp:ListBox id=ListBox1 runat="server" AutoPostBack="True">
</asp:ListBox>
</cc1:TestPanel>
================================================

The code behind for the ASPX has:
================================================
private void Page_Load(object sender, System.EventArgs e)
{
if( ! IsPostBack )
{
ListBox1.Items.Add( new ListItem( "1", "1" ) );
ListBox1.Items.Add( new ListItem( "2", "2" ) );
ListBox1.Items.Add( new ListItem( "3", "3" ) );
ListBox1.Items.Add( new ListItem( "4", "4" ) );
ListBox1.Items.Add( new ListItem( "5", "5" ) );
ListBox1.Items.Add( new ListItem( "6", "6" ) );
}
}

*Also assume that ListBox1 has its SelectedIndexChanged event wired to a
local handler.
================================================


When this ASPX initially loads, everything looks fine. However, if a browser
client then selectes an item in ListBox1 causing a post back, the ListBox
control's SelectedIndexChanged handler doesn't get called and none of the
orginal items are retrieved from ViewState. Inspecting the __VIEWSTATE field
sent to the client after initial page request and then the subsequent
postback showed that value goes from something relatively large to something
arbitrarily small (the digest/hash value of a blank ViewState I assume),
confirming to me that something is causing ViewState not work properly.

To compare, if you change the ASPX page to look like:
================================================
<asp:ListBox id=ListBox1 runat="server" AutoPostBack="True">
</asp:ListBox>
<cc1:TestPanel id=TestPanel1 runat="server">
</cc1:TestPanel>
================================================

.... removing the ListBox from the TestPanel container, things seem to work
fine; SelectedIndexChanged is called, the ListBox retains its items between
requests, and the __VIEWSTATE field doesn't seem to loose its value.





Wilco Bauwer said:
When and how are you creating/adding the controls in your custom
control? Make sure the controls are created upon each request (which
includes postbacks). I suggest override CreateChildControls and
create/add them there. Also override the Controls property, do a call
to EnsureChildControls and then return the base.Controls collection.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,756
Messages
2,569,533
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top