P
Peter Zolja
Hi,
I'm building a webcontrol that contains a dynamic list of other controls. My
problem is that when I add or remove an item the synchronization between the
ViewState and the Controls collection seems to break -- or at least that's
my theory for now.
Here's what I do; to add an item I do that following on the PostBack of a
button:
1. Create a Button object and set its properties
2. Add the object to the Controls collection
3. Increase a counter (saved in ViewState) that will allow me to rebuild the
Controls collection in CreateChildControls in future PostBacks
When I add a button and set its Text property to "something" that text is
rendered to the browser, but it's not saved in the ViewState; as a
consequence that "something" is lost if I do another PostBack (from a
different control). Removing a control that was added dynamically has
similar issues. If I have, let's say 4 controls, and I try to remove the
second, the second control is removed from the Controls collection, but the
ViewState shifts / gets corrupted(?). For example, let's say the 4 controls
look like this:
[ 1 ] [ 2 ] [ 3 ] [ 4 ]
We want to delete the second control; after removing the second control the
output is:
[ 1 ] [ 3 ] [ 4 ]
which is what we want. Unfortunately, on a subsequent PostBack, the list
becomes:
[ 1] [ ] [ 3 ]
which basically tells me that the ViewState got out of sync with the number
of control in the Controls collection.
Is my theory correct, is there a way to fix this problem? I've attached a
simple test project to exemplify my problem.
Any help / suggestion is welcome, thanks!
Peter.
----- WebForms1.aspx ---- (the relevant part)
<form id="Form1" method="post" runat="server">
<aspanel Runat=server ID="pnlTest"></aspanel>
<br>
<asp:Button Runat=server id="btPostBackTest" Text="Test"></asp:Button>
</form>
----- WebForms1.aspx.cs ---- (the relavant pieces)
private void WebForm1_Init(object sender, System.EventArgs e)
{
ControlList cl = new ControlList();
pnlTest.Controls.Add(cl);
}
private void btPostBackTest_Click(object sender, System.EventArgs e)
{
// do nothing... just a postback test
}
----- ControlList.cs ----- (the file that holds the ControlList class)
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
[ ... ]
public class ControlList : WebControl, INamingContainer
{
public ControlList()
{
//
// TODO: Add constructor logic here
//
}
private Button _btAdd;
private Button _btDelete;
public int ItemCount
{
get
{
object count = ViewState["count"];
return ((count == null) ? 0 : Convert.ToInt32(count));
}
set
{
ViewState["count"] = value;
}
}
private TextBox NewButton(int index)
{
TextBox t = new TextBox();
Controls.Add(t);
t.ID = "tb" + index.ToString();
return t;
}
protected void OnAddClick(object sender, System.EventArgs e)
{
ItemCount++;
TextBox t = NewButton(ItemCount);
// at this stage t is already in the Controls collection, but
// the modified text value below doesn't save to the ViewState (why?)
t.Text = "something #" + ItemCount.ToString();
}
protected void OnDeleteClick(object sender, System.EventArgs e)
{
// to make it simple we always try to delete the second textbox
(id=tb1)
Control c = FindControl("tb1");
if (c != null)
{
ItemCount--;
// this seems to messup the sync btw Controls and ViewState
Controls.Remove(c);
}
}
public override ControlCollection Controls
{
get
{
EnsureChildControls();
return base.Controls;
}
}
protected override void CreateChildControls()
{
Controls.Clear();
_btAdd = new Button();
Controls.Add(_btAdd);
_btAdd.ID = "btAdd";
_btAdd.Text = "Add";
_btAdd.Click += new EventHandler(OnAddClick);
_btDelete = new Button();
Controls.Add(_btDelete);
_btDelete.ID = "btDel";
_btDelete.Text = "Delete";
_btDelete.Click += new EventHandler(OnDeleteClick);
for (int i = 0; i < ItemCount; i++)
{
NewButton(i);
}
}
}
I'm building a webcontrol that contains a dynamic list of other controls. My
problem is that when I add or remove an item the synchronization between the
ViewState and the Controls collection seems to break -- or at least that's
my theory for now.
Here's what I do; to add an item I do that following on the PostBack of a
button:
1. Create a Button object and set its properties
2. Add the object to the Controls collection
3. Increase a counter (saved in ViewState) that will allow me to rebuild the
Controls collection in CreateChildControls in future PostBacks
When I add a button and set its Text property to "something" that text is
rendered to the browser, but it's not saved in the ViewState; as a
consequence that "something" is lost if I do another PostBack (from a
different control). Removing a control that was added dynamically has
similar issues. If I have, let's say 4 controls, and I try to remove the
second, the second control is removed from the Controls collection, but the
ViewState shifts / gets corrupted(?). For example, let's say the 4 controls
look like this:
[ 1 ] [ 2 ] [ 3 ] [ 4 ]
We want to delete the second control; after removing the second control the
output is:
[ 1 ] [ 3 ] [ 4 ]
which is what we want. Unfortunately, on a subsequent PostBack, the list
becomes:
[ 1] [ ] [ 3 ]
which basically tells me that the ViewState got out of sync with the number
of control in the Controls collection.
Is my theory correct, is there a way to fix this problem? I've attached a
simple test project to exemplify my problem.
Any help / suggestion is welcome, thanks!
Peter.
----- WebForms1.aspx ---- (the relevant part)
<form id="Form1" method="post" runat="server">
<aspanel Runat=server ID="pnlTest"></aspanel>
<br>
<asp:Button Runat=server id="btPostBackTest" Text="Test"></asp:Button>
</form>
----- WebForms1.aspx.cs ---- (the relavant pieces)
private void WebForm1_Init(object sender, System.EventArgs e)
{
ControlList cl = new ControlList();
pnlTest.Controls.Add(cl);
}
private void btPostBackTest_Click(object sender, System.EventArgs e)
{
// do nothing... just a postback test
}
----- ControlList.cs ----- (the file that holds the ControlList class)
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
[ ... ]
public class ControlList : WebControl, INamingContainer
{
public ControlList()
{
//
// TODO: Add constructor logic here
//
}
private Button _btAdd;
private Button _btDelete;
public int ItemCount
{
get
{
object count = ViewState["count"];
return ((count == null) ? 0 : Convert.ToInt32(count));
}
set
{
ViewState["count"] = value;
}
}
private TextBox NewButton(int index)
{
TextBox t = new TextBox();
Controls.Add(t);
t.ID = "tb" + index.ToString();
return t;
}
protected void OnAddClick(object sender, System.EventArgs e)
{
ItemCount++;
TextBox t = NewButton(ItemCount);
// at this stage t is already in the Controls collection, but
// the modified text value below doesn't save to the ViewState (why?)
t.Text = "something #" + ItemCount.ToString();
}
protected void OnDeleteClick(object sender, System.EventArgs e)
{
// to make it simple we always try to delete the second textbox
(id=tb1)
Control c = FindControl("tb1");
if (c != null)
{
ItemCount--;
// this seems to messup the sync btw Controls and ViewState
Controls.Remove(c);
}
}
public override ControlCollection Controls
{
get
{
EnsureChildControls();
return base.Controls;
}
}
protected override void CreateChildControls()
{
Controls.Clear();
_btAdd = new Button();
Controls.Add(_btAdd);
_btAdd.ID = "btAdd";
_btAdd.Text = "Add";
_btAdd.Click += new EventHandler(OnAddClick);
_btDelete = new Button();
Controls.Add(_btDelete);
_btDelete.ID = "btDel";
_btDelete.Text = "Delete";
_btDelete.Click += new EventHandler(OnDeleteClick);
for (int i = 0; i < ItemCount; i++)
{
NewButton(i);
}
}
}