0

I have a few different custom controls that extend from different web controls:

  • RulesPanel: Panel contains a Table and a few other controls. The Table is populated dynamically with a variable number of RuleRow objects. The other controls include ways to increase the number of rows.
  • RuleRow: TableRow contains three RuleCell objects
  • RuleCell: TableCell contains a Panel which is populated dynamically with a variable number of RuleData controls. It also contains a few other controls outside of the panel, which are used to increase the number of controls inside the panel.
  • RuleData: a control extending from Table that contains a fixed number of cells and controls.

For RulesPanel, I want to save the number of rows it has to the control state. For RulesCell, I want to save the number of controls it has inside its panel. I did it something like this for both (using a table instead of a panel for RulesPanel):

protected override void OnInit(EventArgs e)
{
    Page.RegisterRequiresControlState(this);
    base.OnInit(e);
}
protected override object SaveControlState()
{
    return new Pair(base.SaveControlState(), pnlChildren.Controls.Count);
}
protected override void LoadControlState(object savedState)
{
    Pair controlState = savedState as Pair;
    base.LoadControlState(controlState.First);
    if ((Int32)controlState.Second > pnlChildren.Controls.Count)
    {
        AddChildren((Int32)controlState.Second - pnlChildren.Controls.Count);
    }
}

Edit: Here is the event handler and the AddChildren function it calls for the RuleCell class. The RulesPanel has a similar set of methods, but it adds RuleRow objects to its table rather than RuleData objects to a panel.

protected void btnNeedMore_click(object sender, EventArgs e)
{
    Int32 numToAdd;
    if (Int32.TryParse(txtNeedMore.Text, out numToAdd))
    {
        AddChildren(numToAdd);
        txtNeedMore.Text = "";
    }
}
private void AddChildren(Int32 numberToAdd)
{
    Int32 totalNumber = pnlChildren.Controls.Count + numberToAdd;
    for (Int32 i = pnlChildren.Controls.Count; i < totalNumber; i++)
    {
        pnlChildren.Controls.Add(new RuleData(i));
    }
}

The RuleCell objects behave correctly. However, when I add more RuleRow objects to a RulesPanel (via a postback event from clicking a button), the next postback will cause a "Collection was modified" exception outside of the reach of the VS2012 debugger, just like here.

Collection was modified; enumeration operation may not execute.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[InvalidOperationException: Collection was modified; enumeration operation may not execute.]
   System.Collections.HashtableEnumerator.MoveNext() +10715184
   System.Web.UI.Page.LoadAllState() +292
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1849

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.18055

Why?

Community
  • 1
  • 1
Micteu
  • 451
  • 5
  • 17
  • How are you adding the items? It sounds like you are modifying the `Controls` collection after it has been rendered – codemonkeh Nov 13 '13 at 23:23
  • @codemonkeh I had it loop in a method called by the event handler for a button. I edited the code above to add the two new methods. – Micteu Nov 14 '13 at 14:00
  • You may need to add the controls to the 'Form' controls collection so the control knows about them on postback. – WraithNath Nov 14 '13 at 14:38
  • Does the `RulesPanel` use a `foreach` statement in it's `AddChildren` method? – codemonkeh Nov 14 '13 at 22:28
  • 1
    As an aside, you really should override `CreateChildControls` and only modify the controls collection from there. – codemonkeh Nov 14 '13 at 22:29
  • @codemonkeh It uses a for...next loop like in `RuleCell`. I overrode a bunch of the life cycle methods and put in break points to track what's going on. It looks like when a new `RuleCell` is created from postback, its control state is saved after the `RulesPanel` saves, where the previously-created ones were saved before the `RulesPanel` save. Then on the next postback, it looks like the page unloads immediately after `RulesPanel` loads its control state, without calling OnLoad. Do you suppose using `CreateChildControls` will behave better? – Micteu Nov 15 '13 at 14:33
  • I can't be certain but it is recommended, mainly I suppose because it will be called at the right stage during the page life cycle which is why I would also recommend it. – codemonkeh Nov 18 '13 at 06:18
  • @codemonkey This popped up again. I moved all the parts that modify the `Controls` properties over to `CreateChildControls` like you suggested, and now things work. I didn't before because I was new to WebForms and scared of changing the overall layout. – Micteu Jun 25 '15 at 19:48

0 Answers0