Community discussion forum
Problem with dynamic Tablerows containing controls
-
Hi!
I'm trying to create a page with dynamic controls, and I'm having difficulty wrt the life cycle.
The page has some static controls, but also a table which has dynamic rows, cells of which contain dynamic controls, including a DropDownList.
The idea is that the page has an "Add" button, which should result in a new row being added to the table. The selected values in the earlier DropDownLists must be preserved.
So, first time the page looks like:
[Static Controls]
[TableRow1 [DropDownList1]]
[Add Button]
Clicking the button should result in:
[Static Controls]
[TableRow1 [DropDownList1]]
[TableRow2 [DropDownList2]]
[Add Button]
with DropDownList1 selection being preserved.
The Button click event code tries to read the current selections of the DDLs and store them in session, It then causes a redirect to the same page, forcing the page to be drawn as new (from the life cycle point of view).
My sequence of code is:
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
populate();
}
populateVolatile();
}
where populate() sets up the static controls with data retrieved from a database (first time) or session (subsequently), and
populateVolatile() creates and fills the dynamic controls depending on the session data, including DDL selections.
It contains the code:
// add each type/num info to table
foreach (TypeNumInfo tni in volInfo.typeNumRows) {
TableRow tr = new TableRow();
// index number
TableCell tc1 = new TableCell();
tc1.Text = tni.idx.ToString();
tr.Cells.Add(tc1);
// add a DDL
TableCell tc2 = new TableCell();
DropDownList ddl = new DropDownList();
// add ID and list data
ddl.ID = "ddl" + tni.idx.ToString();
foreach (ListItem li in dTypes) {
ddl.Items.Add(li);
}
// set the selected
// TODO not working
ddl.SelectedIndex = tni.typeSel;
tc2.Controls.Add(ddl);
tr.Cells.Add(tc2);
// add row to table
tblTypeNum.Rows.Add(tr);
}
The button click code is as follows:
protected void bAddTypeNum_Click(object sender, EventArgs e) {
// get dept, applies selections from static controls
statInfo.selDept = ddlDept.SelectedIndex;
statInfo.selApplies = ddlApplies.SelectedIndex;
// create new type/num info representing tablerow data
TypeNumInfo tni = new TypeNumInfo();
int lastIdx = volInfo.typeNumRows.Count;
tni.idx = lastIdx + 1;
// add to list
volInfo.typeNumRows.Add(tni);
// cache previous rows' info
int i = 0;
foreach (TableRow tr in tblTypeNum.Rows) {
// if row has droplist, i.e. more than 1 cell
if (tr.Cells.Count > 1) {
DropDownList ddl = (DropDownList)tr.Cells[1].Controls[0];
// get the selection in that row
// TODO not working reliably
volInfo.typeNumRows[i++].typeSel = ddl.SelectedIndex;
}
}
// update session
Session[strStat] = statInfo;
Session[strVol] = volInfo;
// draw as new page
Response.Redirect("");
}
statInfo and volInfo are sessionised objects that contain static and dynamic control data and state.
I know the redirect creates an extra trip to the server, but I couldn't find a more useful way to enable redrawing after the button event.
Everything works fine except for the dynamic DDL selection handling - I cannot set or correctly read the selections.
Help!
John
-
Can you provide the error being thrown John, or perhaps a better explanation over what happens when the table is populated. I've just replicated your code and all seems well:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim rows As Integer = 9Dim table As New Table() Dim row As TableRowDim cell As TableCell Dim dropdown As DropDownListFor i As Integer = 0 To 9row =
New TableRow()cell = New TableCell()cell.Text =
"SomeText"row.Cells.Add(cell)
cell = New TableCell()dropdown =
New DropDownList()dropdown.ID = "DropDown" + i.ToString For j As Integer = 0 To 5dropdown.Items.Add("Item " + j.ToString) Nextcell.Controls.Add(dropdown)
row.Cells.Add(cell)
table.Rows.Add(row)
NextPlace1.Controls.Add(table)
End Sub -
[quote user="D'Scouser"]
Can you provide the error being thrown John, or perhaps a better explanation over what happens when the table is populated. I've just replicated your code and all seems well:
[/quote]
Hiya,
No error thrown - it just doesn't draw the DDLs with the correct selections after button click. I've used the debugger to step through, the correct values are being extracted from session and the assignment seems to go ok. But when the page appears, all the DDLs display as selection 0;
Interestingly, when the button is clicked a second time to produce a third row, the loop to extract the selection values from the DDLs pulls the same value from the first two DDLs, even though the input was for different values. This makes me think that the user input is not filtering through correctly, or that the system is somehow using the same memory DDL instance for the three on the page or their contained lists. Debugger won't let me look inside the DDLs with enough detail. It made me think perhaps I'm not using the life-cycle correctly.
I placed the table in a Panel on the static page - should I be doing something else, such as a Placeholder in each DDL cell? I'm not clear on the significance of Panels and Placeholders wrt functionality.
-
Can't you save an ArrayList in Session, and then type it out when needed. I'm imagining something is going wrong saving the values to state. By the way, have you set Session state in the config file?
-
[quote user="D'Scouser"]
Can't you save an ArrayList in Session, and then type it out when needed. I'm imagining something is going wrong saving the values to state. By the way, have you set Session state in the config file?
[/quote]
Hiya,
I'm just using the default session state, but that seems to be fine. I am indeed using a sessionised list to populate the DDLs, and that part is working fine.
It's only the selection assignment and read that's giving the problem.
I've pruned down the code and substituted the DB access with strings, so I'm showing it here in its entirety. It's not too long, not enough to embarrass me hopefully!
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;
using System.Collections.Generic;
public partial class DocSearchAdvanced : System.Web.UI.Page {
protected StatInfo statInfo;
protected VolInfo volInfo;
protected List<ListItem> dTypes;
private String strVol = "Volatile";
private String strStat = "Static";
private String strDocType = "dtypes";
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
populate();
}
populateVolatile();
}
// populates the static controls and sets up once-only info
protected void populate() {
// get info from session if exists
statInfo = (StatInfo)Session[strStat];
// if first time, set up static info
if (statInfo == null) {
statInfo = new StatInfo();
// set defaults
statInfo.selDept = 0;
statInfo.selApplies = 0;
// cache to session
Session[strStat] = statInfo;
}
// department and applies droplist - contain same information
ddlDept.Items.Add(new ListItem("Any", "0"));
ddlApplies.Items.Add(new ListItem("Any", "0"));
// add "departments", as if from DB
for (int i=1; i<5; i++) {
StringBuilder sb = new StringBuilder();
sb.Append("Dept").Append(i.ToString()).Append(" (").Append("D").Append(i.ToString()).Append(")");
ddlDept.Items.Add(new ListItem(sb.ToString(), i.ToString()));
ddlApplies.Items.Add(new ListItem(sb.ToString(), i.ToString()));
}
// set current selections
ddlDept.SelectedIndex = statInfo.selDept;
ddlApplies.SelectedIndex = statInfo.selApplies;
// document types
dTypes = (List<ListItem>)Session[strDocType];
// if first time, set up doc types as if from DB
if (dTypes == null) {
dTypes = new List<ListItem>();
dTypes.Add(new ListItem("Any", "0"));
for (int i=1; i<5; i++) {
StringBuilder sb = new StringBuilder();
sb.Append("DocType").Append(i.ToString()).Append(" (").Append("DT").Append(i.ToString()).Append(")");
dTypes.Add(new ListItem(sb.ToString(), i.ToString()));
}
// cache it
Session[strDocType] = dTypes;
}
}
protected void getSession() {
// get static data if in postback
if (IsPostBack) {
statInfo = (StatInfo)Session[strStat];
dTypes = (List<ListItem>)Session[strDocType];
}
// get dynamic typenum info from session if exists
volInfo = (VolInfo)Session[strVol];
// first time?
if (volInfo == null) {
// need to create volinfo and add first typenum row
volInfo = new VolInfo();
List<TypeNumInfo> typeRows = new List<TypeNumInfo>();
TypeNumInfo tni = new TypeNumInfo();
// index is 1, selection 0
tni.idx = 1;
tni.typeSel = 0;
typeRows.Add(tni);
volInfo.typeNumRows = typeRows;
// update session
Session[strVol] = volInfo;
}
}
protected void populateVolatile() {
if (IsPostBack) {
tblTypeNum.Rows.Clear();
}
// get the session data
getSession();
// add each typenum info to table
foreach (TypeNumInfo tni in volInfo.typeNumRows) {
TableRow tr = new TableRow();
// index number
TableCell tc1 = new TableCell();
tc1.Text = tni.idx.ToString();
tr.Cells.Add(tc1);
// add a DDL
TableCell tc2 = new TableCell();
DropDownList ddl = new DropDownList();
// add ID and list data
ddl.ID = "ddl" + tni.idx;
foreach (ListItem li in dTypes) {
ddl.Items.Add(li);
}
// set the selected
// TODO not working
ddl.SelectedIndex = tni.typeSel;
tc2.Controls.Add(ddl);
tr.Cells.Add(tc2);
TableCell tc3 = new TableCell();
tc3.Text = "Should be " + ddl.SelectedItem.Text;
tr.Cells.Add(tc3);
tblTypeNum.Rows.Add(tr);
}
// add row with button
TableRow trb = new TableRow();
TableCell tcb = new TableCell();
int rwidth = tblTypeNum.Rows[0].Cells.Count;
tcb.ColumnSpan = rwidth; // colspan as first row
Button bAddTypeNum = new Button();
bAddTypeNum.Text = "Add New Row";
bAddTypeNum.Click += new EventHandler(bAddTypeNum_Click);
tcb.Controls.Add(bAddTypeNum);
trb.Cells.Add(tcb);
tblTypeNum.Rows.Add(trb);
}
// add typenum row
protected void bAddTypeNum_Click(object sender, EventArgs e) {
// get dept, applies selections from static controls
statInfo.selDept = ddlDept.SelectedIndex;
statInfo.selApplies = ddlApplies.SelectedIndex;
// create new type/num info representing tablerow data
TypeNumInfo tni = new TypeNumInfo();
int lastIdx = volInfo.typeNumRows.Count;
tni.idx = lastIdx + 1;
// add to list
volInfo.typeNumRows.Add(tni);
// cache previous rows
int i = 0;
foreach (TableRow tr in tblTypeNum.Rows) {
// if row has droplist, i.e. more than 1 cell
if (tr.Cells.Count > 1) {
DropDownList ddl = (DropDownList)tr.Cells[1].Controls[0];
// get the selected in that row
// TODO not working reliably
volInfo.typeNumRows[i++].typeSel = ddl.SelectedIndex;
}
}
// update session
Session[strStat] = statInfo;
Session[strVol] = volInfo;
// draw as new page
Response.Redirect("");
}
protected void bSearch_Click(object sender, EventArgs e) {
// TODO
}
// class for type/num rows
protected class TypeNumInfo {
public int idx;
public int typeSel;
}
// class for static data
protected class StatInfo {
// Department[] depts;
public int selDept;
public int selApplies;
}
// class for volatile control data
protected class VolInfo {
public List<TypeNumInfo> typeNumRows;
}
}
... and the markup, in case the problem is there...<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DocSearchAdvanced.aspx.cs"
Inherits="DocSearchAdvanced" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel ID="PanelEdit" runat="server">
<table border="0" cellpadding="0" cellspacing="0" style="width: 800px">
<tr>
<td style="width: 200px; height: 19px;">
Department</td>
<td style="width: 200px; height: 19px;">
Applies to</td>
</tr>
<tr>
<td style="width: 200px">
<asp:DropDownList ID="ddlDept" runat="server" Width="172px">
</asp:DropDownList></td>
<td style="width: 200px">
<asp:DropDownList ID="ddlApplies" runat="server">
</asp:DropDownList></td>
</tr>
<tr>
<td colspan="2" style="height: 163px">
<asp:Table ID="tblTypeNum" runat="server" Height="144px" Width="741px" BorderColor="Black"
BorderWidth="1px" GridLines="Both">
</asp:Table>
</td>
</tr>
<tr>
<td style="width: 200px">
</td>
<td style="width: 200px">
<asp:Button ID="bSearch" runat="server" OnClick="bSearch_Click" Text="Search" /></td>
</tr>
</table>
<br />
</asp:Panel>
</div>
</form>
</body>
</html>
-
Hey John, I think I know the problem by looking at how your setting up your session store, because I think I've run into this problem before. But I'll just check. Does the values saved in the store not come out correctly, or completely disappear when doing a postback?
-
Try setting the session store in the page load before a postback; I'll just use my code, but the rule is the same regardless:
PageLoad
SetupDataStore()
If Not Postback Then
Do your population here...
Else
Do your popvolatile here...
End If
End Sub
Sub SetupDataStore()
If Me.Session("products") Is Nothing Then
dtOrders = CheckoutClass.BuildTempColumns()
Me.Session("products") = dtOrders
Else
dtOrders = CType(Me.Session("products"), Data.DataTable)
End IfEnd Sub
-
I think you need to test your datastore before your postback, try this method. I'll use my code, but the rule is basically the same:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
SetDataStore()
If Not IsPostBack Then
Do your populate here...
Else
Do your volatilepop here...
End If
End SubPrivate Sub SetDataStore()
If Me.Session("products") Is Nothing Then
dtOrders = CheckoutClass.BuildTempColumns()
Me.Session("products") = dtOrders
Else
dtOrders = CType(Me.Session("products"), Data.DataTable)
End If
End SubHope this helps, but there may be other issues depending on the kind of retrieval your doing. Feel free to get back to me if other problems arise.
-
Not sure how that happened!
-
Hiya,
Datastore is working fine as far as I can tell. I've taken the debugger right through it, and all the values are extracted correctly. The code more or less does what you're suggesting, StaticPop is called first time, followed by volatilePop. Only volatilePop is called at postback.
The staticPop happens once per new or repeat-redirected page, and this sets up the static data for session first time round, and subsequently uses it for the static DDLs. That's working fine.
The volatilePop calls its own getSession, which sets up its data first time in, and retrieves it from session each subsequent call. Again, this all seems to work fine - the dynamic DDLs are being correctly populated.Using the debugger to step through, checking what's retrieved from session, everything seems to be OK. The selection assignment appears to complete in the code, it's just when the page appears that the dynamic DDL selections are not set. They are still fully populated.
-
RESOLVED!
The page lifecycle concept is fine - the problem is in the nature of the DropDownList control. Fixed by closer RTFM!
The DDL is a container for ListItems which represent the items in the droplist. When one is selected, the selection information is stored as a boolean in that particular ListItem, not in the DDL container.
As I have several DDLs containing the same information, they all used the same instances of ListItems. So when one DDL selection was made, the same selection reflected in the other DDLs. The solution is to create a new instance of each master ListItem and adding that to the DDL container, not the master ones themselves. So each DDL has its own unique ListItem instances, albeit reflecting the same underlying data.
Post a reply
Related discussion
-
Issue with modalpopup (ajax) control
by askm95 (0 replies)
-
A simple Login Application for a newbie
by joe90 (5 replies)
-
How to create dynamic textboxes and get its value to store them in db(its very urgent)
by Archu136 (13 replies)
-
problem in validations
by yogeshkadvekar (5 replies)
-
Using ADO.NET with SQL Server
by Manjot Bawa (23 replies)
Quick links
Recent activity
- karez bartolo replied to integrating barcode reader ...
- david freezea replied to Better integration solution...
- vincent golding replied to London Focus Groups - earn £80
- Claude Kornelis replied to jQuery - tablesorter paging...
- anzo mangr replied to Share an all-in-one Video C...
- sebrina queller replied to Black Friday Deals: Great P...
Enter your message below
Sign in or Join us (it's free).