Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic User Controls get and maintain values after postbacks

I have a page that I have built that is dynamically generating user controls. For example, the user control has two fields that can be edited: SomeId and SomeData. I want to be able to set these values based on a UserControlObject. When I read the control afterwards, I want to be able to return a UserControlObject from the control and set the database values to that. I am having some trouble getting the values to stay current.

//Page
public class SomePage
{
    private List<MyUserControl> _MyUserControls;

    private void InitControls()
    {
      var controlsToBuild = SomeDatabaseCallToGetControlObjects();

          foreach(MyUserControl control in controlsToBuild)
          {
        MyUserControl control = this.Page.LoadControl("MyControl.ascx") as MyUserControl;

          UserControlPlaceHolder.Controls.Add(myControl);
          _MyUserControls.Add(myControl);
      }
    }

    protected void Page_Init(object sender, EventArgs e)
    {
       InitControls();
    }

    protected void SaveButton_Click(object sender, EventArgs e)
    {
      if (this.IsValid)
      {
        List<MyUserObject>listObj = new List<MyUserObject>();
        foreach(MyUserControl control in _MyUserControls)
        {
           MyUserObject obj = control.GetObjectFromControl();
           listObjs.Add(obj);
        }
        SaveToDatabase(listObjs);
        //Clear placeholder in case number of controls has changed based on the save
        UserControlPlaceHolder.Clear();
        InitControls();
        GetObjectFromDatabase();
      }
    }

}

//Object to put in control
public class UserControlObject
{
    public string DataForUser { get;set;}
    public double MoreDataForUser { get;set;}
    public int HiddenIdFromUser { get; set;}
}


//User control
public class MyUserControl : UserControl
{

  public MyUserControlObject GetObjectFromControl()
  {
     UserControlObject obj = new UserControlObject();
     obj.DataForUser = textBoxOnTheControl.Text;
     obj.MoreDataForUser = anotherBoxOnTheControl.Text;
     obj.HiddenIdFromUser = hiddenField.Value;

     return obj;
  }

  public void SetUserControlObject(UserControlObject obj)
  {
     textBoxOnTheControl.Text = obj.DataForUser;
     anotherBoxOnTheControl.Text = obj.MoreDataForUser;
     hiddenField.Value = obj.HiddenIdFromUser;
  }
}

Basically, I want to load up each MyUserControl with values from a database. A user should be able to change most (but not all) of those fields. A user shouldn't be able to change the ID, but I need it to update the record in the database.

I am having some trouble getting the information to save and persist correctly, because I am re-creating a collection of MyUserControl. It seems to work fine on the inital postback, but after subsequent postbacks, I still get the old value for one of the fields. How can I ensure that:

1) The information is set to the user control(s) correctly. Do I need to put a public property referencing viewstate on every property of the UserControl?

2) After clicking a save event, after postback, rebuilding the controls won't cause me to retain any "old" values from the initial load? How should I handle the viewstate here? I want the data to reflect whatever the user has changed.

like image 610
Bryan Crosby Avatar asked Mar 25 '11 04:03

Bryan Crosby


People also ask

How can we keep dynamic controls on PostBack in asp net?

In order to retain the dynamic TextBoxes across PostBacks, we need to make use of Page's PreInit event to recreate the dynamic TextBoxes using the Request. Form collection. First all the keys containing the string txtDynamic are fetched and then for each key the CreateTextBox method is called.

What are dynamic controls in asp net?

The DynamicControl control is used by templated data-bound controls, such as FormView or ListView, to display a data field that uses ASP.NET Dynamic Data features in a custom page. You can also use a DynamicControl control in a TemplateField field of a GridView or a DetailsView control.


2 Answers

For view-state/control-state to work properly, you must have same ID for your controls. As you are not assigning ID explicitly, it must getting generated and there would be conflicts there with more than one user control being present. I will recommend you to have some kind of logic to generate same set of IDs over post-back. For example,

private void InitControls()
{
  var numControlsToBuild = SomeDatabaseCallToGetNumControls();
  int idCounter = 1;
  foreach(var controlData in numControlsToBuild)
  {
    var control = this.Page.LoadControl("MyControl.ascx") as MyUserControl;
    control.ID = "MyControl" + idCounter.ToString();
    idCounter++;
    UserControlPlaceHolder.Controls.Add(myControl);
    _MyUserControls.Add(control);

    // probably code load control data into control
  }
}
like image 92
VinayC Avatar answered Oct 20 '22 19:10

VinayC


As pointed out by VinayC, the fundamental problem here is due to the fact that when you dynamically add controls to a page, if you do not assign IDs for those controls, they will be given generated IDs. If the number of dynamically generated controls changes, or if the order in which they are created changes, then there will be a mismatch between the dynamically created web controls and the state information for those objects in the view state. Similarly, there will be a mismatch between the IDs of the dynamically created web controls and the IDs of query parameters in the POST headers. That means that the values of some fields will not be updated from the POST data.

View state ID mismatches will cause things like labels and literals to revert to initial values. On the other hand, POST data ID mismatches will cause input fields to revert to inital values. If you are having problems with input fields reverting to initial values, mucking around with the view state will not solve the problem. It is a common misconception that field values are persisted in the view state. However, this is unnecessary because input field values are already contained in the POST data. For a good discussion of how the View State works, see Understanding how the viewstate works by Scott Mitchell.

If you always create your dynamically added controls in the same order then, as VinayC points out, you can generate a sequence of IDs such as "ID1", "ID2", "ID3", ... as you are creating the web controls. In your case, since you are storing your User Control field values in a database, you might want to generate new IDs for each user control only once, and store each generated ID in the database along with the associated field values. That way, you needn't be concerned about the order in which you create the controls.

like image 22
Joel Lee Avatar answered Oct 20 '22 19:10

Joel Lee