Viewstate and Dynmac controls

  • Thread starter Thread starter Steve Drake
  • Start date Start date
S

Steve Drake

All,

I have a CONTROL that contains 1 control (Control ONE), the 1 control that
it can contain 1 or 2 control (Control A and B).

Control A, raises and event and Control ONE receives this event and this
causes control B to be created, when this is done the VIEWSTATE is lost for
CONTROL B.

In the EVENT that causes CONTROL B to be created I have to set
ChildControlsCreated to false, if I don't set it the viewstate works but the
control doesn't show, as the createchild controls does not get re-ran.

Below is the a smaller version of the code that I have created to reproduce
the problem test as follows :

Click Get Current Time, it will now show the time
Click do post back, it will still show time in first step, as well as time
of post back, to prove its posted back.
Click Raise Event, it has now lost the viewstate, time has gone blank.
Click Get Current Time on both line, you will see the time get filled in and
it persisted in the viewstate, do a few clicks to prove this.
Now Click Raise Event, view state has been lost again.

Cheers

Steve

Project name should be ViewStateTest

Code : (Add to single class called controls.cs)

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ViewStateTest
{
public class ContainTime : WebControl ,INamingContainer
{
ShowTime showtimeA;
ShowTime showtimeB;

WebControl holdingcontrol;

bool showB;

protected override void LoadViewState(object savedState)
{

base.LoadViewState (savedState);
if (ViewState["ShowB"] == null)
{
showB = false;
}
else
{
showB = (bool) ViewState["ShowB"];
}

}
protected override object SaveViewState()
{
ViewState["ShowB"] = showB;
return base.SaveViewState ();
}

protected override void CreateChildControls()
{
Controls.Clear();
showtimeA = new ShowTime();
showtimeA.Click+=new EventHandler(showtimeA_Click);
Controls.Add(showtimeA);

if (showB )
{
showtimeB = new ShowTime();
Controls.Add(showtimeB);
}
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
showtimeA.RenderControl(writer);
if (showB) showtimeB.RenderControl(writer);
}

public ContainTime()
{

}

private void showtimeA_Click(object sender, EventArgs e)
{
this.showB = !showB;
this.ChildControlsCreated = false; // if I dont do this, the control is
there
// its the above line that makes the viewstate vanish, but I need it to
cause the controls to dynamicly add.
}
}

/// <summary>
/// Summary description for ShowTime.
/// </summary>
public class ShowTime : WebControl,INamingContainer
{
public ShowTime()
{
}

string timestring;
Button abutton;
Button getTimeButton;
Button doRoundTrip;

Label timelabel;

public event EventHandler Click;

protected override void LoadViewState(object savedState)
{
base.LoadViewState (savedState);
System.Diagnostics.Trace.WriteLine("Items in viewstate " +
ViewState.Count);

if (ViewState["time"] == null)
{
timestring = "Not found";
}
else
{
timestring = (string) ViewState["time"];
}
}
protected override object SaveViewState()
{
ViewState["time"] = timestring;
return base.SaveViewState ();
}

protected override void CreateChildControls()
{
// For this example we could just write the time out in the render, but I
need to mimic the class
// that I am having problems with as much as possable.
Controls.Clear();
timelabel = new Label();
timelabel.Text = timestring;
abutton = new Button();
abutton.Text = "Raise Event";
abutton.Click+=new System.EventHandler(abutton_Click);

getTimeButton = new Button();
getTimeButton.Text = "Get Current Time";
getTimeButton.Click+=new EventHandler(getTimeButton_Click);

doRoundTrip = new Button();
doRoundTrip.Text = "Do Post Back";
doRoundTrip.Click+=new EventHandler(doRoundTrip_Click);

Controls.Add(timelabel);
Controls.Add(abutton);
Controls.Add(getTimeButton);
Controls.Add(doRoundTrip);
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
writer.RenderBeginTag("DIV");
writer.Write(this.UniqueID);
EnsureChildControls();
timelabel.RenderControl(writer);
abutton.RenderControl(writer);
getTimeButton.RenderControl(writer);
writer.Write("Time of post back " + System.DateTime.Now.ToString());
doRoundTrip.RenderControl(writer);
writer.RenderEndTag();
}

private void abutton_Click(object sender, System.EventArgs e)
{
Click(this,e);
}

private void getTimeButton_Click(object sender, EventArgs e)
{
this.timestring = System.DateTime.Now.ToString();
ChildControlsCreated=false;
}

private void doRoundTrip_Click(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("Round trip");
ChildControlsCreated=false;
}
}
}


CODE BEHIND CODE

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace ViewStateTest
{
/// <summary>
/// Summary description for WebForm1.
/// </summary>
public class WebForm1 : System.Web.UI.Page
{
protected ViewStateTest.ContainTime ContainTime1;

private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
}

#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);

}
#endregion
}
}


ASPX PAGE :


<%@ Register TagPrefix="cc1" Namespace="ViewStateTest"
Assembly="ViewStateTest" %>
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false"
Inherits="ViewStateTest.WebForm1" smartNavigation="True"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm1</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema"
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<cc1:ContainTime id="ContainTime1" style="Z-INDEX: 101; LEFT: 56px;
POSITION: absolute; TOP: 48px"
runat="server"></cc1:ContainTime>
</form>
</body>
</HTML>
 
Dynamicaly added controls are "lost" on each request. They
do actually save their viewstate to the page but on the
next submit they do not read/load it because they're lost,
they dont exist during LoadViewState because you probably
create them later in the page lifecycle like in an
eventhanlder or in Page_Load.

In order for this to work you have to somehow add your
dynamic controls before ViewState is loaded for example in
Page_Init, but another problem arises from this. Let's say
you create some controls and save in your UserControls
ViewState that these controls are created. How to know
which controls you have created since you can't read your
own ViewState in Page_Init? That means you should probably
override the LoadViewState method. I did something like
this I think(!), it was pretty simple and I think it
worked. I overrided LoadViewState. In LoadViewState I
called base.LoadViewState() checked the ViewState to see
which controls I have created, added/created them again
and called base.LoadViewState() with the child controls
created again :) I'm not sure it's the best solution or
that even it will work properly but I hope this helped you
understand the problem...

Sometimes it is much more simple to keep your controls
static and invisible than bother with this though..

-----Original Message-----
All,

I have a CONTROL that contains 1 control (Control ONE), the 1 control that
it can contain 1 or 2 control (Control A and B).

Control A, raises and event and Control ONE receives this event and this
causes control B to be created, when this is done the VIEWSTATE is lost for
CONTROL B.

In the EVENT that causes CONTROL B to be created I have to set
ChildControlsCreated to false, if I don't set it the viewstate works but the
control doesn't show, as the createchild controls does not get re-ran.

Below is the a smaller version of the code that I have created to reproduce
the problem test as follows :

Click Get Current Time, it will now show the time
Click do post back, it will still show time in first step, as well as time
of post back, to prove its posted back.
Click Raise Event, it has now lost the viewstate, time has gone blank.
Click Get Current Time on both line, you will see the time get filled in and
it persisted in the viewstate, do a few clicks to prove this.
Now Click Raise Event, view state has been lost again.

Cheers

Steve

Project name should be ViewStateTest

Code : (Add to single class called controls.cs)

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ViewStateTest
{
public class ContainTime : WebControl ,INamingContainer
{
ShowTime showtimeA;
ShowTime showtimeB;

WebControl holdingcontrol;

bool showB;

protected override void LoadViewState(object savedState)
{

base.LoadViewState (savedState);
if (ViewState["ShowB"] == null)
{
showB = false;
}
else
{
showB = (bool) ViewState["ShowB"];
}

}
protected override object SaveViewState()
{
ViewState["ShowB"] = showB;
return base.SaveViewState ();
}

protected override void CreateChildControls()
{
Controls.Clear();
showtimeA = new ShowTime();
showtimeA.Click+=new EventHandler(showtimeA_Click);
Controls.Add(showtimeA);

if (showB )
{
showtimeB = new ShowTime();
Controls.Add(showtimeB);
}
}
protected override void Render
(System.Web.UI.HtmlTextWriter writer)
{
showtimeA.RenderControl(writer);
if (showB) showtimeB.RenderControl(writer);
}

public ContainTime()
{

}

private void showtimeA_Click(object sender, EventArgs e)
{
this.showB = !showB;
this.ChildControlsCreated = false; // if I dont do this, the control is
there
// its the above line that makes the viewstate vanish, but I need it to
cause the controls to dynamicly add.
}
}

/// <summary>
/// Summary description for ShowTime.
/// </summary>
public class ShowTime : WebControl,INamingContainer
{
public ShowTime()
{
}

string timestring;
Button abutton;
Button getTimeButton;
Button doRoundTrip;

Label timelabel;

public event EventHandler Click;

protected override void LoadViewState(object savedState)
{
base.LoadViewState (savedState);
System.Diagnostics.Trace.WriteLine("Items in viewstate " +
ViewState.Count);

if (ViewState["time"] == null)
{
timestring = "Not found";
}
else
{
timestring = (string) ViewState["time"];
}
}
protected override object SaveViewState()
{
ViewState["time"] = timestring;
return base.SaveViewState ();
}

protected override void CreateChildControls()
{
// For this example we could just write the time out in the render, but I
need to mimic the class
// that I am having problems with as much as possable.
Controls.Clear();
timelabel = new Label();
timelabel.Text = timestring;
abutton = new Button();
abutton.Text = "Raise Event";
abutton.Click+=new System.EventHandler(abutton_Click);

getTimeButton = new Button();
getTimeButton.Text = "Get Current Time";
getTimeButton.Click+=new EventHandler (getTimeButton_Click);

doRoundTrip = new Button();
doRoundTrip.Text = "Do Post Back";
doRoundTrip.Click+=new EventHandler(doRoundTrip_Click);

Controls.Add(timelabel);
Controls.Add(abutton);
Controls.Add(getTimeButton);
Controls.Add(doRoundTrip);
}
protected override void Render
(System.Web.UI.HtmlTextWriter writer)
 
These are purely done in server controls, they are created in the correct
order ect and the viewstates does work, but .... the viewstats gets zapped
when a control is added.

This is how my controls are laid out (this looks fine in OE with default
font, if it don't look OK cut and paste the NOTEPAD version at the end.

*********************************
* PAGE *
* *
* **************************** *
* * CONTROL ONE * *
* * * *
* * **************** * *
* * * CONTROL A * * *
* * * [BUTTON] * * *
* * **************** * *
* * * *
* * **************** * *
* * * CONTROL B * * *
* * * [BUTTON] * * *
* * **************** * *
* **************************** *
*********************************

Control B is not created at the start, I click the button on A, CONTROL A
raises an event, CONTROL process the event and stores a flag in the
viewstate to tell CONTROL ONE to create a CONTROL B (this flag keeps its
viewstate ok) , and it creates control B, but control A loses its viewstate
(ONLY CONTROL A) , i then click the button in control B, this does a post
back on all control keep there viewstate.

A work around is to store the viewstat for CONTROL A and B in CONTROL ONE,
this does work, but is going to be tricky todo in other server controls.

I am starting to think that this is just the way it works, you need to test
the code yourself to see the problem.

NOTEPAD VERSION (cut and paste me into notepad)

---- start cut ----
*********************************
* PAGE *
* *
* *************************** *
* * CONTROL ONE * *
* * * *
* * **************** * *
* * * CONTROL A * * *
* * * [BUTTON] * * *
* * **************** * *
* * * *
* * **************** * *
* * * CONTROL B * * *
* * * [BUTTON] * * *
* * **************** * *
* *************************** *
*********************************
---- end cut ----


Adrijan Josic said:
Dynamicaly added controls are "lost" on each request. They
do actually save their viewstate to the page but on the
next submit they do not read/load it because they're lost,
they dont exist during LoadViewState because you probably
create them later in the page lifecycle like in an
eventhanlder or in Page_Load.

In order for this to work you have to somehow add your
dynamic controls before ViewState is loaded for example in
Page_Init, but another problem arises from this. Let's say
you create some controls and save in your UserControls
ViewState that these controls are created. How to know
which controls you have created since you can't read your
own ViewState in Page_Init? That means you should probably
override the LoadViewState method. I did something like
this I think(!), it was pretty simple and I think it
worked. I overrided LoadViewState. In LoadViewState I
called base.LoadViewState() checked the ViewState to see
which controls I have created, added/created them again
and called base.LoadViewState() with the child controls
created again :) I'm not sure it's the best solution or
that even it will work properly but I hope this helped you
understand the problem...

Sometimes it is much more simple to keep your controls
static and invisible than bother with this though..

-----Original Message-----
All,

I have a CONTROL that contains 1 control (Control ONE), the 1 control that
it can contain 1 or 2 control (Control A and B).

Control A, raises and event and Control ONE receives this event and this
causes control B to be created, when this is done the VIEWSTATE is lost for
CONTROL B.

In the EVENT that causes CONTROL B to be created I have to set
ChildControlsCreated to false, if I don't set it the viewstate works but the
control doesn't show, as the createchild controls does not get re-ran.

Below is the a smaller version of the code that I have created to reproduce
the problem test as follows :

Click Get Current Time, it will now show the time
Click do post back, it will still show time in first step, as well as time
of post back, to prove its posted back.
Click Raise Event, it has now lost the viewstate, time has gone blank.
Click Get Current Time on both line, you will see the time get filled in and
it persisted in the viewstate, do a few clicks to prove this.
Now Click Raise Event, view state has been lost again.

Cheers

Steve

Project name should be ViewStateTest

Code : (Add to single class called controls.cs)

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ViewStateTest
{
public class ContainTime : WebControl ,INamingContainer
{
ShowTime showtimeA;
ShowTime showtimeB;

WebControl holdingcontrol;

bool showB;

protected override void LoadViewState(object savedState)
{

base.LoadViewState (savedState);
if (ViewState["ShowB"] == null)
{
showB = false;
}
else
{
showB = (bool) ViewState["ShowB"];
}

}
protected override object SaveViewState()
{
ViewState["ShowB"] = showB;
return base.SaveViewState ();
}

protected override void CreateChildControls()
{
Controls.Clear();
showtimeA = new ShowTime();
showtimeA.Click+=new EventHandler(showtimeA_Click);
Controls.Add(showtimeA);

if (showB )
{
showtimeB = new ShowTime();
Controls.Add(showtimeB);
}
}
protected override void Render
(System.Web.UI.HtmlTextWriter writer)
{
showtimeA.RenderControl(writer);
if (showB) showtimeB.RenderControl(writer);
}

public ContainTime()
{

}

private void showtimeA_Click(object sender, EventArgs e)
{
this.showB = !showB;
this.ChildControlsCreated = false; // if I dont do this, the control is
there
// its the above line that makes the viewstate vanish, but I need it to
cause the controls to dynamicly add.
}
}

/// <summary>
/// Summary description for ShowTime.
/// </summary>
public class ShowTime : WebControl,INamingContainer
{
public ShowTime()
{
}

string timestring;
Button abutton;
Button getTimeButton;
Button doRoundTrip;

Label timelabel;

public event EventHandler Click;

protected override void LoadViewState(object savedState)
{
base.LoadViewState (savedState);
System.Diagnostics.Trace.WriteLine("Items in viewstate " +
ViewState.Count);

if (ViewState["time"] == null)
{
timestring = "Not found";
}
else
{
timestring = (string) ViewState["time"];
}
}
protected override object SaveViewState()
{
ViewState["time"] = timestring;
return base.SaveViewState ();
}

protected override void CreateChildControls()
{
// For this example we could just write the time out in the render, but I
need to mimic the class
// that I am having problems with as much as possable.
Controls.Clear();
timelabel = new Label();
timelabel.Text = timestring;
abutton = new Button();
abutton.Text = "Raise Event";
abutton.Click+=new System.EventHandler(abutton_Click);

getTimeButton = new Button();
getTimeButton.Text = "Get Current Time";
getTimeButton.Click+=new EventHandler (getTimeButton_Click);

doRoundTrip = new Button();
doRoundTrip.Text = "Do Post Back";
doRoundTrip.Click+=new EventHandler(doRoundTrip_Click);

Controls.Add(timelabel);
Controls.Add(abutton);
Controls.Add(getTimeButton);
Controls.Add(doRoundTrip);
}
protected override void Render
(System.Web.UI.HtmlTextWriter writer)
{
writer.RenderBeginTag("DIV");
writer.Write(this.UniqueID);
EnsureChildControls();
timelabel.RenderControl(writer);
abutton.RenderControl(writer);
getTimeButton.RenderControl(writer);
writer.Write("Time of post back " + System.DateTime.Now.ToString());
doRoundTrip.RenderControl(writer);
writer.RenderEndTag();
}

private void abutton_Click(object sender, System.EventArgs e)
{
Click(this,e);
}

private void getTimeButton_Click(object sender, EventArgs e)
{
this.timestring = System.DateTime.Now.ToString();
ChildControlsCreated=false;
}

private void doRoundTrip_Click(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("Round trip");
ChildControlsCreated=false;
}
}
}


CODE BEHIND CODE

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace ViewStateTest
{
/// <summary>
/// Summary description for WebForm1.
/// </summary>
public class WebForm1 : System.Web.UI.Page
{
protected ViewStateTest.ContainTime ContainTime1;

private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
}

#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);

}
#endregion
}
}


ASPX PAGE :


<%@ Register TagPrefix="cc1" Namespace="ViewStateTest"
Assembly="ViewStateTest" %>
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false"
Inherits="ViewStateTest.WebForm1" smartNavigation="True"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm1</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema"
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<cc1:ContainTime id="ContainTime1" style="Z-INDEX: 101; LEFT: 56px;
POSITION: absolute; TOP: 48px"
runat="server"></cc1:ContainTime>
</form>
</body>
</HTML>







.
 
I fixed this as follows (note new BASE CLASS), comments welcome:

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ViewStateTest
{
public class myControl : WebControl
{
internal object mySaveViewState()
{
return this.SaveViewState ();
}
internal void myLoadViewState(object savedState)
{
this.LoadViewState (savedState);
}
internal void myTrackViewState()
{
this.TrackViewState();
}
}

public class ContainTime : myControl,INamingContainer
{

showtime showtimeA=new showtime();
showtime showtimeB=new showtime();



System.Web.UI.WebControls.Calendar calA;
System.Web.UI.WebControls.Calendar calB;

WebControl holdingcontrol;

bool showB;



protected override void LoadViewState(object savedState)
{
if (savedState!=null)
{
object[] states = (object[]) savedState;

if(states[0] != null)
base.LoadViewState(states[0]);
if(states[1] != null)
showtimeA.myLoadViewState(states[1]);
if(states[2] != null)
showtimeB.myLoadViewState(states[2]);


}

/* Load Custom View State here */
object o = ViewState["ShowB"] ;
if (o== null)
showB = false;
else
showB = (bool) o;

}
protected override object SaveViewState()
{

ViewState["ShowB"] = showB;

object[] states = new object [3];
states[0] = base.SaveViewState();
states[1] = (showtimeA== null ? null : showtimeA.mySaveViewState());
states[2] = (showtimeB== null ? null : showtimeB.mySaveViewState());
for(int i=0; i < states.Length; i++)
{
if(states!=null)
return states;
}
return null;
}
protected override void CreateChildControls()
{
Controls.Clear();
showtimeA.ID = "SA";
showtimeB.ID = "SB";

showtimeA.Click+=new EventHandler(showtimeA_Click);
Controls.Add(showtimeA);
calA = new Calendar();
Controls.Add(calA);

// if (showB )
{
calB = new Calendar();
Controls.Add(showtimeB);
Controls.Add(calB);
}
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
showtimeA.RenderControl(writer);
calA.RenderControl(writer);
if (showB) showtimeB.RenderControl(writer);
if (showB) calB.RenderControl(writer);
}


protected override void TrackViewState()
{
base.TrackViewState ();
}

public ContainTime()
{
showtimeA=new showtime();
showtimeB=new showtime();

}

private void showtimeA_Click(object sender, EventArgs e)
{
this.showB = !showB;
this.ChildControlsCreated = false;
}
}

/// <summary>
/// Summary description for showtime.
/// </summary>
public class showtime : myControl,INamingContainer
{
public showtime()
{
}

string timestring;
Button abutton;
Button getTimeButton;
Button doRoundTrip;

Label timelabel;

public event EventHandler Click;

protected override void LoadViewState(object savedState)
{
base.LoadViewState (savedState);
System.Diagnostics.Trace.WriteLine("Items in viewstate " +
ViewState.Count);

if (ViewState["time"] == null)
{
timestring = "Not found";
}
else
{
timestring = (string) ViewState["time"];
}
}
protected override object SaveViewState()
{
ViewState["time"] = timestring;
return base.SaveViewState ();
}

protected override void CreateChildControls()
{
// For this example we could just write the time out in the render, but I
need to mimic the class
// that I am having problems with as much as possable.
Controls.Clear();
timelabel = new Label();
timelabel.Text = timestring;
abutton = new Button();
abutton.Text = "Raise Event";
abutton.Click+=new System.EventHandler(abutton_Click);

getTimeButton = new Button();
getTimeButton.Text = "Get Current Time";
getTimeButton.Click+=new EventHandler(getTimeButton_Click);

doRoundTrip = new Button();
doRoundTrip.Text = "Do Post Back";
doRoundTrip.Click+=new EventHandler(doRoundTrip_Click);

Controls.Add(timelabel);
Controls.Add(abutton);
Controls.Add(getTimeButton);
Controls.Add(doRoundTrip);
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
writer.RenderBeginTag("DIV");
writer.Write(this.UniqueID);
EnsureChildControls();
timelabel.RenderControl(writer);
abutton.RenderControl(writer);
getTimeButton.RenderControl(writer);
writer.Write("Time of post back " + System.DateTime.Now.ToString());
doRoundTrip.RenderControl(writer);
writer.RenderEndTag();
}

private void abutton_Click(object sender, System.EventArgs e)
{
base.TrackViewState ();

Click(this,e);
}

private void getTimeButton_Click(object sender, EventArgs e)
{
this.timestring = System.DateTime.Now.ToString();
ChildControlsCreated=false;
}

private void doRoundTrip_Click(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("Round trip");
ChildControlsCreated=false;
}
}
}

Steve Drake said:
All,

I have a CONTROL that contains 1 control (Control ONE), the 1 control that
it can contain 1 or 2 control (Control A and B).

Control A, raises and event and Control ONE receives this event and this
causes control B to be created, when this is done the VIEWSTATE is lost for
CONTROL B.

In the EVENT that causes CONTROL B to be created I have to set
ChildControlsCreated to false, if I don't set it the viewstate works but the
control doesn't show, as the createchild controls does not get re-ran.

Below is the a smaller version of the code that I have created to reproduce
the problem test as follows :

Click Get Current Time, it will now show the time
Click do post back, it will still show time in first step, as well as time
of post back, to prove its posted back.
Click Raise Event, it has now lost the viewstate, time has gone blank.
Click Get Current Time on both line, you will see the time get filled in and
it persisted in the viewstate, do a few clicks to prove this.
Now Click Raise Event, view state has been lost again.

Cheers

Steve

Project name should be ViewStateTest

Code : (Add to single class called controls.cs)

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ViewStateTest
{
public class ContainTime : WebControl ,INamingContainer
{
ShowTime showtimeA;
ShowTime showtimeB;

WebControl holdingcontrol;

bool showB;

protected override void LoadViewState(object savedState)
{

base.LoadViewState (savedState);
if (ViewState["ShowB"] == null)
{
showB = false;
}
else
{
showB = (bool) ViewState["ShowB"];
}

}
protected override object SaveViewState()
{
ViewState["ShowB"] = showB;
return base.SaveViewState ();
}

protected override void CreateChildControls()
{
Controls.Clear();
showtimeA = new ShowTime();
showtimeA.Click+=new EventHandler(showtimeA_Click);
Controls.Add(showtimeA);

if (showB )
{
showtimeB = new ShowTime();
Controls.Add(showtimeB);
}
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
showtimeA.RenderControl(writer);
if (showB) showtimeB.RenderControl(writer);
}

public ContainTime()
{

}

private void showtimeA_Click(object sender, EventArgs e)
{
this.showB = !showB;
this.ChildControlsCreated = false; // if I dont do this, the control is
there
// its the above line that makes the viewstate vanish, but I need it to
cause the controls to dynamicly add.
}
}

/// <summary>
/// Summary description for ShowTime.
/// </summary>
public class ShowTime : WebControl,INamingContainer
{
public ShowTime()
{
}

string timestring;
Button abutton;
Button getTimeButton;
Button doRoundTrip;

Label timelabel;

public event EventHandler Click;

protected override void LoadViewState(object savedState)
{
base.LoadViewState (savedState);
System.Diagnostics.Trace.WriteLine("Items in viewstate " +
ViewState.Count);

if (ViewState["time"] == null)
{
timestring = "Not found";
}
else
{
timestring = (string) ViewState["time"];
}
}
protected override object SaveViewState()
{
ViewState["time"] = timestring;
return base.SaveViewState ();
}

protected override void CreateChildControls()
{
// For this example we could just write the time out in the render, but I
need to mimic the class
// that I am having problems with as much as possable.
Controls.Clear();
timelabel = new Label();
timelabel.Text = timestring;
abutton = new Button();
abutton.Text = "Raise Event";
abutton.Click+=new System.EventHandler(abutton_Click);

getTimeButton = new Button();
getTimeButton.Text = "Get Current Time";
getTimeButton.Click+=new EventHandler(getTimeButton_Click);

doRoundTrip = new Button();
doRoundTrip.Text = "Do Post Back";
doRoundTrip.Click+=new EventHandler(doRoundTrip_Click);

Controls.Add(timelabel);
Controls.Add(abutton);
Controls.Add(getTimeButton);
Controls.Add(doRoundTrip);
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
writer.RenderBeginTag("DIV");
writer.Write(this.UniqueID);
EnsureChildControls();
timelabel.RenderControl(writer);
abutton.RenderControl(writer);
getTimeButton.RenderControl(writer);
writer.Write("Time of post back " + System.DateTime.Now.ToString());
doRoundTrip.RenderControl(writer);
writer.RenderEndTag();
}

private void abutton_Click(object sender, System.EventArgs e)
{
Click(this,e);
}

private void getTimeButton_Click(object sender, EventArgs e)
{
this.timestring = System.DateTime.Now.ToString();
ChildControlsCreated=false;
}

private void doRoundTrip_Click(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("Round trip");
ChildControlsCreated=false;
}
}
}


CODE BEHIND CODE

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace ViewStateTest
{
/// <summary>
/// Summary description for WebForm1.
/// </summary>
public class WebForm1 : System.Web.UI.Page
{
protected ViewStateTest.ContainTime ContainTime1;

private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
}

#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);

}
#endregion
}
}


ASPX PAGE :


<%@ Register TagPrefix="cc1" Namespace="ViewStateTest"
Assembly="ViewStateTest" %>
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false"
Inherits="ViewStateTest.WebForm1" smartNavigation="True"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm1</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema"
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<cc1:ContainTime id="ContainTime1" style="Z-INDEX: 101; LEFT: 56px;
POSITION: absolute; TOP: 48px"
runat="server"></cc1:ContainTime>
</form>
</body>
</HTML>
 
Back
Top