1
votes

Background

I have a User Control (an .ascx file) which is being dynamically inserting into an asp:PlaceHolder control on a page. That User Control contains an asp:Repeater, which I'm binding to a DataTable.

Theoretically, on the User Control's first load the DataTable is initialized and 3 empty rows are added. A button on the User Control adds additional empty rows to the Repeater, one at a time.

Problem

The issue is that after any PostBack event on the page (namely the button in this example being clicked), the DataTable for the Repeater is empty.

User Control (.ascx) (simplified)

<asp:TextBox ID="controlOutsideRepeater" runat="server" />
<asp:Repeater ID="myRepeater" runat="server">
  <ItemTemplate>
    <p><asp:Textbox ID="firstControlInRepeater" runat="server" text='<%# DataBinder.Eval(Container.DataItem, "A") %>' /></p>
    <p><asp:Textbox ID="secondControlInRepeater" runat="server" text='<%# DataBinder.Eval(Container.DataItem, "B") %>' /></p>
  </ItemTemplate>
</asp:Repeater>
<asp:LinkButton ID="addItemButton" runat="server" Text="Add Item" onclick="addNewItem" />

Code Behind (.ascx.cs) (also simplified)

public DataTable items {
  get {
    object i = ViewState["items"];
    if (i == null) {
      DataTable t = new DataTable();
      t.Columns.Add("A");
      t.Columns.Add("B");
      // add 3 blank items/rows:
      t.Rows.Add(t.NewRow());
      t.Rows.Add(t.NewRow());
      t.Rows.Add(t.NewRow());
      ViewState["items"] = t;
      return t;
    } else {
      return (DataTable)i;
    }
  set { ViewState["items"] = value; }
}

protected void Page_Init(object sender, EventArgs e) {
  myRepeater.DataSource = this.items;
  myRepeater.DataBind();
}

public void addNewItem(object sender, EventArgs e) {
  DataRow r = this.items.NewRow();
  this.items.Rows.Add(r);
  myRepeater.DataBind();
}

Behavior

The first time the UserControl is loaded, the Repeater contains 3 empty items: good! However, after entering some text in the textboxes both inside and outside the repeater and clicking the "Add Item" LinkButton, the page does a refresh/postback and shows 4 empty items, however the textbox -outside- the Repeater retains it's text. Clicking the "Add Item" LinkButton again also performs a postback and still shows 4 empty items, yet the TextBox outside the Repeater again retains it's text.

My Crazy Guess

I've tried wrapping the Repeater databinding in a (!Page.IsPostBack), but this prevented the Repeater from -ever- being bound, as the UserControl is only programmatically added to the page after a PostBack (a button on the Page adds the UserControl on a click, and then the Page checks each PostBack to see if there should be a user control present and re-adds it to the Page if needed). So I'm guessing there's a problem with the Page re-creating the User Control on every PostBack, but can't explain why the TextBox outside the Repeater would retain it's value, and why the ViewState doesn't seem to remember my item (on each postback ViewState["items"] is null and gets re-built within the getter).

HELP!

1

1 Answers

1
votes

The problem is you are data binding every single request when really you only want to data bind on the first request. Since you don't data bind on the first page load, you will have to check if you are data bound in a way other than !Page.IsPostBack. You could add a property to your user control to handle this and then check against that every page load / page init.

Update: With more details from comments

I see your AddItem() now. I've had problems using viewstate this way though I'm not entirely sure why. I've had to do it more like the following:

public void addNewItem(object sender, EventArgs e) {
  DataTable theItems = this.items;
  DataRow r = theItems.NewRow()
  theItems.Rows.Add(r);
  this.items = theItems
  myRepeater.DataBind(); //I'm not sure if this belongs here because of the reasons said before
}