I am trying to get an application to allow a user to select something for a number rows and then do something on the server side once the user submits the form. The problem I'm having is that if I reload the table, I just get the default values back and if I don't, the table is empty. Here is the code:
<asp:Table ID="tbl" runat="server">
<asp:TableRow>
<asp:TableHeaderCell>Question</asp:TableHeaderCell>
<asp:TableHeaderCell>Answer</asp:TableHeaderCell>
</asp:TableRow>
</asp:Table>
c#:
protected System.Web.UI.WebControls.Table tbl1;
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
init_tbl();
}
}
protected void init_tbl()
{
tbl1.BorderWidth = 1;
TableRow tr = new TableRow();
tc = new TableCell();
tc.Text = "text";
tc.BorderWidth = 1;
tr.Cells.Add(tc);
ddl = new DropDownList();
ddl.ID = "r" + index;
ddl.Attributes["runat"] = "server";
ListItem item;
for (int i = 1; i <= 10; i++)
{
item = new ListItem();
item.Text = i.ToString();
item.Value = i.ToString();
if (i.ToString().Equals(r.Trim()))
{
item.Selected = true;
}
ddl.Items.Add(item);
}
list.Add(ddl);
tc.Controls.Add(ddl);
tc.ID = "tblr" + index;
tr.Cells.Add(tc);
tbl1.Rows.Add(tr);
}
your problem is with the convoluted way asp.net deals with dynamic controls, you need to create the dynamic control on page init, before the view state is set so state of the controls is maintained on the post, see this article http://geekswithblogs.net/shahed/archive/2008/06/26/123391.aspx.
You should definatley look at the gridview or at the very least one of the repeater controls.
I would just make a list of questions and DataBind them to a repeater/gridview/datagrid (as #Cybernate said), then add an Event method to the OnItemDataBound of the databinder.
In the ItemDataBound event I would get a list of answers for each question DataItem and add them to a DropDownList like you were doing above.
When the user fills out all of the answers you just need to go through the Request.Form array and find all the answers that will be passed back.
You stored the Table in the ViewState and Use it as you want.
Related
I have a Table dynamically generated and inside of 2 columns there are TextBoxes. During the postback the Textboxes are always empty even if I fill them.
The table is builted in this way:
protected void ddlScalaTaglie_SelectedIndexChanged(object sender, EventArgs e)
{
DataTable dtRighe = oTab.getScaleTaglieRighe(ddlScalaTaglie.SelectedValue);
//dt is datatable object which holds DB results.
Table tbl = new Table();
tbl.CssClass = "table table-striped table-bordered table-responsive";
TableRow tr;
TableCell tcEU, tcUS, tcUK, tcQty, tcEAN;
Label lbEU, lbUS, lbUK, lbQty, lbEAN;
TextBox tbEU, tbUS, tbUK, tbQty, tbEAN, tbToFocus = null;
foreach (DataRow dr in dtRighe.Rows)
{
tr = new TableRow();
//ean
tcEAN = new TableCell();
tbEAN = new TextBox();
tbEAN.ID = "txtEAN" + dr[0].ToString();
tbEAN.Width = new Unit(100, UnitType.Percentage);
tbEAN.Columns = 15;
tcEAN.Controls.Add(tbEAN);
tr.Controls.Add(tcEAN);
//Qty
tcQty = new TableCell();
tbQty = new TextBox();
tbQty.ID = "txtQty" + dr[0].ToString();
tbQty.TextMode = TextBoxMode.Number;
tcQty.Controls.Add(tbQty);
tr.Controls.Add(tcQty);
tbl.Controls.Add(tr);
}
Session["tbl"] = tbl;
divTaglieRighe.Controls.Add(tbl);
}
When I click the button SAVE, I have to loop throug my table and save all the TextBox.Text...
this is what I wrote:
ArrayList arrScarpaFigli = new ArrayList();
Table tbl = (Table)Session["tbl"];
foreach (TableRow row in tbl.Rows)
{
foreach (TableCell cell in row.Cells)
{
foreach (Control ctrl in cell.Controls)
{
//CONTROL IS TEXBOXT: EXTRACT VALUES//
if (ctrl is TextBox)
{
TextBox txt = (TextBox)ctrl;
arrScarpaFigli.Add(txt.Text);
}
}
}
}
The problem is that, also if I fill the textboxes, in my Text there is always an empty string.
The only moment when I fill the table is on the selectionChange of a Dropdown.
What I'm doing wrong?
Thanks in advance
You have to recreate dynamic controls on every page load to make sure their values are retained after a PostBack. So you need to create a new Method that handles the creation of the Table every time the page is loaded.
protected void Page_Load(object sender, EventArgs e)
{
//always create the controls on every page load if there is a value selected in ddlScalaTaglie
if (!string.IsNullOrEmpty(ddlScalaTaglie.SelectedValue))
{
createTable(ddlScalaTaglie.SelectedValue);
}
}
protected void Button1_Click(object sender, EventArgs e)
{
//use findcontrol to find the Table inside the placeholder
Table tbl = Page.FindControl("divTaglieRighe").FindControl("Table1") as Table;
//loop all rows and cells in the Table
foreach (TableRow row in tbl.Rows)
{
foreach (TableCell cell in row.Cells)
{
foreach (Control ctrl in cell.Controls)
{
//the control is a textbox
if (ctrl is TextBox)
{
//cast the control back to a textbox
TextBox tb = ctrl as TextBox;
//does the checkbox have a value, if so append the label
if (!string.IsNullOrEmpty(tb.Text))
{
Label1.Text += tb.Text + "<br>";
}
}
}
}
}
}
protected void ddlScalaTaglie_SelectedIndexChanged(object sender, EventArgs e)
{
//no need to create the Table dynamically, that will be handled in Page_Load
//this method is just a dummy to trigger a PostBack
//you could remove this method and the OnSelectedIndexChanged from the DropDown
//and just keep the AutoPostBack="true", that will also work
}
private void createTable(string value)
{
DataTable dtRighe = Common.LoadFromDB();
//create a new table WITH id, that is needed for findcontrol
Table tbl = new Table();
tbl.ID = "Table1";
//loop all rows in the datatable
foreach (DataRow dr in dtRighe.Rows)
{
TableRow tr = new TableRow();
//ean
TableCell tcEAN = new TableCell();
TextBox tbEAN = new TextBox();
tbEAN.ID = "txtEAN" + dr[0].ToString();
tbEAN.Width = new Unit(100, UnitType.Percentage);
tbEAN.Columns = 15;
tcEAN.Controls.Add(tbEAN);
tr.Controls.Add(tcEAN);
//Qty
TableCell tcQty = new TableCell();
TextBox tbQty = new TextBox();
tbQty.ID = "txtQty" + dr[0].ToString();
tbQty.TextMode = TextBoxMode.Number;
tcQty.Controls.Add(tbQty);
tr.Controls.Add(tcQty);
tbl.Controls.Add(tr);
}
//add the table to the placeholder
divTaglieRighe.Controls.Add(tbl);
}
The aspx to make the example complete
<asp:DropDownList ID="ddlScalaTaglie" runat="server" OnSelectedIndexChanged="ddlScalaTaglie_SelectedIndexChanged" AutoPostBack="true">
<asp:ListItem Text="Select..." Value=""></asp:ListItem>
<asp:ListItem Text="Option 1" Value="1"></asp:ListItem>
<asp:ListItem Text="Option 2" Value="2"></asp:ListItem>
</asp:DropDownList>
<br />
<br />
<asp:PlaceHolder ID="divTaglieRighe" runat="server"></asp:PlaceHolder>
<br />
<br />
<asp:Button ID="Button1" runat="server" Text="Save" OnClick="Button1_Click" />
<br />
<br />
<asp:Label ID="Label1" runat="server" Text=""></asp:Label>
Storing an instance of Table control in Session is a terrible idea and is just plain wrong. Controls belong to their parent page - they should be added to it as soon as they are created and they should die with their parent page.
The thing with dynamically created controls in ASP.NET is that you have to store an indicator of the fact that they were created (together with relevant information needed to create them again) and recreate them on all subsequent postbacks - no later than in Page_Load. Only then these controls will have the chance of getting their values from Request and you will have the chance of obtaining these values from controls.
In your code, the instance of Table stored in Session (together with all its rows, cells and textboxes) will never reflect any client-side data changes, nor will it belong to Pages that will be created to process subsequent postback requests.
Update
The important fact to understand here is that, after processing the postback caused by ddlScalaTaglie change and returning hew html to the client, the page instance is gone. On next request a new instance of your page is created - and it does not know anything about the fact that the previous instance had table added to its control tree. It is your code in Page_Load that must discover that the form must have this table, and create the table in exactly the same way it was created the first time - with rows, cells and textboxes with exactly the same IDs.
Then, after you add this newly created table to divTaglieRighe.Controls, the textboxes will be able to extract their client-side values from Request.Form collection.
Make sure the textbox has a "name" for the post back to the controller.
If it does not it grabs a null for the parameter.
Check Request.Form for data returned to controller.
You've created a Table called tbl and added two TextBox, then you store Table control in session Session["tbl"] and added to divTaglieRighe control. Finally you are trying to get TextBox data from your session Session["tbl"] table not the ViewState control that added in divTaglieRighe control. You have to get data from you Table tbl control that added in divTaglieRighe control.
Hello everybody and thanks in advance,
I´ve read several threads about but I'm very confused. The thing is I'm dynamically creating some textboxes inside a panel control which is placed inside a formview whith this code...
protected void Muestra_Precio_Stock_BT_Click(object sender, EventArgs e)
{
CheckBoxList FormatosCL = (CheckBoxList)FormViewDiscos.FindControl("FormatosCL");
Panel Precio_Stock_PN = (Panel)FormViewDiscos.FindControl("CB_Precio_Stock_PN");
Label NuevoPrecioLB = null;
TextBox NuevoPrecioTB = null;
Label NuevoStockLB = null;
TextBox NuevoStockTB = null;
foreach (ListItem item in FormatosCL.Items)
{
if(item.Selected)
{
NuevoPrecioLB = new Label();
NuevoPrecioTB = new TextBox();
NuevoStockLB = new Label();
NuevoStockTB = new TextBox();
NuevoPrecioLB.ID = "NuevoPrecioLB_" + FormatosCL.Items.IndexOf(item);
NuevoPrecioLB.Text = "PRECIO " + item.Text + ": ";
NuevoPrecioTB.ID = "NuevoPrecioTB_" + FormatosCL.Items.IndexOf(item);
NuevoStockLB.ID = "NuevoStockLB_" + FormatosCL.Items.IndexOf(item);
NuevoStockLB.Text = " STOCK " + item.Text + ": ";
NuevoStockTB.ID = "NuevoStockTB_" + FormatosCL.Items.IndexOf(item);
Precio_Stock_PN.Controls.Add(NuevoPrecioLB);
Precio_Stock_PN.Controls.Add(NuevoPrecioTB);
Precio_Stock_PN.Controls.Add(NuevoStockLB);
Precio_Stock_PN.Controls.Add(NuevoStockTB);
Precio_Stock_PN.Controls.Add(new LiteralControl("<br />"));
}
}
}
Well, this works well, when I run the project the textboxes and labels are created as expected, I can see them and write into the textboxes. I`ve put the same code into Page_Load event and now I can get the created controls and avoid the nullpointerexception (that was my first problem) when I click the INSERT button of the formview. The problem is that, in the following code...
protected void InsertButton_Click(object sender, EventArgs e)
{
TextBox DiscIdTB = (TextBox)FormViewDiscos.FindControl("DiscIdTB");
TextBox CaratulaTB = (TextBox)FormViewDiscos.FindControl("CaratulaTB");
CheckBoxList CancionesCL = (CheckBoxList)FormViewDiscos.FindControl("CancionesCL");
CheckBoxList FormatosCL = (CheckBoxList)FormViewDiscos.FindControl("FormatosCL");
using (SqlConnection conexion = new SqlConnection(ConfigurationManager.ConnectionStrings["ConexionBBDD-Discos"].ConnectionString))
{
conexion.Open();
cmd.Connection = conexion;
cmd.CommandText = "INSERT INTO DISCOS_FORMATOS (ID_DISCO, ID_FORMATO, PRECIO, STOCK) VALUES ((SELECT MAX(ID_DISCO) FROM DISCOS), #IdFormato, 0, 0)";
foreach (ListItem item in FormatosCL.Items)
{
if (item.Selected)
{
Panel Precio_Stock_PN = (Panel)FormViewDiscos.FindControl("CB_Precio_Stock_PN");
string control = "NuevoPrecioTB_" + (FormatosCL.Items.IndexOf(item));
string control2 = "NuevoStockTB_" + (FormatosCL.Items.IndexOf(item));
TextBox Precio = (TextBox)Precio_Stock_PN.FindControl(control);
TextBox Stock = (TextBox)Precio_Stock_PN.FindControl(control2);
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#IdFormato", item.Value);
cmd.Parameters.AddWithValue("#IdPrecio", Convert.ToInt32(Precio.Text));
cmd.Parameters.AddWithValue("#IdStock", Convert.ToInt32(Stock.Text));
cmd.ExecuteNonQuery();
}
}
conexion.Close();
cmd.Dispose();
}
}
when the execution reaches these lines, dynamically created textboxes lost their values, returning always 0...
cmd.Parameters.AddWithValue("#IdPrecio", Convert.ToInt32(Precio.Text));
cmd.Parameters.AddWithValue("#IdStock", Convert.ToInt32(Stock.Text));
How can I preserve their user typed values?
Have you tried putting the code in the Page_Load method inside a block with
if (!IsPostBack)
{
//
}
Another thing you can try if this doesn't work is saving the typed-in values in Session variables.
I don't think the InsertButton is important here. By the way, if you use the runat=server on the objects you don't have to use FindControl, the server will be able to manage it. Also for dynamic controls you might want to try a different approach like: GridView (remove headers and it won't look like a grid), a DataList, or a ListView. What goes inside the ItemTemplate are the objects you are repeating/creating dynamically. You can add labels and textboxes here. For example:
<asp:ListView ID="repeatedStuff" runat="server"><ItemTemplate>
</ItemTemplate>
</asp:ListView>
Or
<asp:DataListID="list"
CellPadding="2"
RepeatDirection="Vertical"
RepeatLayout="Table"
RepeatColumns="2"
runat="server">
<ItemTemplate>
<asp:CheckBoxID="cboxP"AutoPostBack="True"runat="server"/>
<asp:Labelrunat="server"Text='<%#Bind("Nombre")%>'ID='<%#Bind("ID")%>'></asp:Label>
</ItemTemplate>
</asp:DataList>
What remains after this approach is managing the binding and how to identify them when an event is triggered. Like adding a textchange event on server side, if you bind the ID value on the event you ask for the sender's ID and then you can know which one is being "textchanged".
In my webpage, I have Calendar, Table and button.
After selecting date, it will fire the databind() method of table. There are checkboxes with autopostback =true. Once checked, the Table disappears. I have no idea on how to retain the table with the checked checkboxes after post back.
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString.Get("Id") != null)
{
if (!IsPostBack)
{
Calendar1.Visible = false;
}
}
}
protected void Calendar1_SelectionChanged(object sender, EventArgs e)
{
Label1.Text = Calendar1.SelectedDate.ToShortDateString();
//Set datasource = (cal.selectedDate), the invoking override
// DataBind() method to create table
}
Calendar1.Visible = false;
}
I've tried to databind the table again else (IsPostBack) but i wasn't able to achieve my goals, instead, it created another table on top of the existing table
This is the method to create Table with checkboxes
public override void DataBind()
{
TableRow myTableRow = default(TableRow);
TableCell myTableCell = default(TableCell);
if (source != null && !(mDate == DateTime.MinValue))
{
for (int i = 0; i <= 23; i++)
{
foreach (DataRow row in source.Tables["Object"].Rows)
{
myTableCell = new TableCell();
CheckBox cb = new CheckBox();
cb.AutoPostBack = true;
cb.Attributes.Add("id", row["objid"].ToString());
cb.InputAttributes.Add("rowID", mDate.Date.AddHours(i).ToString());
myTableCell.Controls.Add(cb);
myTableCell.HorizontalAlign = HorizontalAlign.Center;
myTableRow.Cells.Add(myTableCell);
TimeSheetTable.Rows.Add(myTableRow);
}
}
}
else
{
throw new ArgumentException(" Invalid Date.");
}
}
Dynamically generated tables need to be regenerated on every postback. For subsequent postbacks, viewstate will be reloaded, but you have to recreate the table, cells, and controls in the same exact fashion, otherwise web forms complains about it. You need to do this during Init I believe; if checkbox checked status changed, the web forms framework will update the Checked property after load, so that will be taken care of.
I usually use a repeater or listview control as dynamic controls can be painful and the ListView is pretty flexible. Databinding takes care of rebuilding the control tree for you.
I have created few textbox dynamically while coding in the flow I have provided unique id for each and I have hard coded some values to all the text boxes using C#.
Now on click of button am trying to retrieve the values from the textbox for which I have used the below code, but its throwing an exception as OBJECT REFERENCE NOT SET TO INSTANCE OF AN OBJECT.
Please look at the below code, I have tried both the things but still am not getting. Please help me out.
Thanks
protected void btnPltGrap_onclick(object sender, EventArgs e)
{
//spny is my stack panel and txtX0 is my of the text box id
//Below is the 1st Try
TextBox tb = new TextBox();
tb= (TextBox)Master.FindControl("spnY").FindControl("txtX0");
string strx = tb.Text;
//Below is the 2nd Try
string strx = (spnY.FindControl("txtX0") as TextBox).Text;
}
Thanks
Am trying to use view state as per you told that i shlould recreate the controls ones again but am getting exception as Invalid Arguments. please go have a look.
protected void btnSet_onClick(object sender, EventArgs e)
{
Table tblMainY = new Table();
TableRow tblRow = new TableRow();
tblMainY.Controls.Add(tblRow);
TableCell tblCel = new TableCell();
TextBox txtdyn = new TextBox();
txtdyn.Text = "1";
txtdyn.ID = "txtY01";
txtdyn.Width = 50;
tblCel.Controls.Add(txtdyn);
tblRow.Controls.Add(tblCel);
splY.Controls.Add(tblMainY);
ViewState["temptbl"] = tblMainY
}
protected void btnPltGrap_onclick(object sender, EventArgs e)
{
splY.Controls.Add(ViewState["Temptbl"]);
}
Please help me out
I've had the same problem in the past.
What I did was give the dynamically-added control an ID, and made sure it retained that ID also on postback.
Once the postbacked control has the same ID as as before, Microsoft did magic and refilled the controls with the pre-postback values.
Read out this code once
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
this.NumberOfControls = 0; //very first time when page is loaded, value will be 0
else
this.createControls(); //if it is postback it will recreate the controls according to number of control has been created
}
//this is the base of this, it will hold the number of controls has been created, called properties
protected int NumberOfControls
{
get { return (int)ViewState["NumControls"]; }
set { ViewState["NumControls"] = value; }
}
//it will create the controls
protected void createControls()
{
int count = this.NumberOfControls;
for (int i = 0; i < count; i++) //loop for the total number of control.
{
TextBox tx = new TextBox(); //creating new control
tx.ID = "ControlID_" + i.ToString(); //in your solution you are giving static id, don't do that, assign id number dynamically, it will help you further, if you want to manipulate the controls for some other use
//Add the Controls to the container of your choice
form1.Controls.Add(tx);
}
}
//add new control
protected void addSomeControl()
{
TextBox tx = new TextBox();
tx.ID = "ControlID_" + NumberOfControls.ToString();
form1.Controls.Add(tx);
this.NumberOfControls++; //increment the number of control
}
protected void AddBtn_Click(object sender, EventArgs e)
{
addSomeControl();
}
Default.aspx
take placeholder tag in aspx file
< asp:PlaceHolder ID="PlaceHolder1" runat="server">
Default.aspx.cs
// adding/creating dynamic text box
TextBox txt = new TextBox();
txt.ID = "New_txt";
txt.TextMode = TextBoxMode.MultiLine;
txt.Text = dt.Rows[0]["message"].ToString();
txt.Width = 802;
txt.Height = 450;
txt.ReadOnly = true;
PlaceHolder1.Controls.Add(txt);
Retrive value from text box
string str = txt.Text;
some sample code in bellow link as my blog
Its explain for how to put and get textboxe's with values and validations in dynamicaly using panel control .
Let's go this url . and you can get good solutions
get and Create dynamic Textbox and dropdownlist with Validation
simple line for get textbox values in
TextBox objTextBox = (TextBox)PlaceHolder.FindControl("CorrecttextBoxName");
string value=objTextBox .Text;
You must recreate your controls on init to get it's value.
Here are some links
Get text from dynamically created textbox in asp.net
Edit 1
TextBox tb=(TextBox)ViewState["Temptbl"];
splY.Controls.Add(tb);
There are lots of examples on this, and I'm pretty sound with the concept of using recursion to find the control. Once the control has been found on postback, it can be interacted with, etc.
I have an empty asp:table in my HTML markup:
<asp:Table ID="editDataTable" runat="server">
</asp:Table>
And on Page_Load the table is populated with many rows and many columns (I am quite proud that I figured that out). Inside some of the table cells there is a <asp:TextBox />.
You've guessed it, I need to get the value of these text boxes!
(I've got the code for recursion and checked it, and it seems good.)
My table has two columns. The left one contains titles like "Company Name, Telephone", etc. and the right column contains text boxes with its respective title's value. So a user can edit the text box (if the telephone number has change for example) and submit the changes.
Obviously the rows are dynamically added depending on the user.
The problem I'm having is: You need to ADD the control to the page when populating the table. Something along the lines of:
myTable.Control.Add(new TextBox());
In my case, my Table is called editDataTable. So in my code where I add the Rows, I've added the control too, as follows.
for (int i = 0; i < rowCount; i++)
{
editDataTable.Rows.Add(tblRow[j]); // This is where I add the ROW to my sexy table
editDataTable.Controls.Add(new TextBox()); // This is where I add the control
}
Those awake amongst you will know that you can't add a text box control to a table!
So finally, my questions are:
How do I add the control for The Text Boxes in my table?
Where do I add them?
Is there any extra advice to help me fulfill my quest of retrieving the text values of my dynamically added text boxes?
Here's my recursive code just in case you were curious:
private void getControls(Control parent)
{
foreach (Control c in parent.Controls)
{
if (c is TextBox && c.ID == null)
{
//Stuff
}
if (c.Controls.Count > 0)
{
getControls(c);
}
}
}
Here is an example of building a dynamic table in ASP.NET during PageLoad, and reading in the values through a postback. Since the table is dynamic, it will NOT be rebuilt when the page is postback to the server. If you want to rebuild the table, you'll need to render it again and use the values you pull in from Request.Form to re-populate it.
HTML Markup
<asp:Table ID="editDataTable" runat="server">
</asp:Table>
<asp:Button runat="server" Text="Submit" OnClick="Submit_Click" />
Code Markup
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string[] dynamicTable = { "First Name", "Last Name", "Address", "Phone" };
foreach (string s in dynamicTable)
{
TableRow row = new TableRow();
// Add First Cell with Text to Row
row.Cells.Add(new TableCell() { Text = s });
// Create Second Cell
TableCell textBoxCell = new TableCell();
// Add Textbox Control to Second Cell
textBoxCell.Controls.Add(new TextBox() { ID = "Dynamic_" + s.Replace(" ","_") });
// Add Second Cell to Row
row.Cells.Add(textBoxCell);
// Add New Row to Table
editDataTable.Rows.Add(row);
}
}
}
protected void Submit_Click(object sender, EventArgs e)
{
for (int i = 0; i < Request.Form.Count; i++)
{
string key = Request.Form.GetKey(i);
if (key.Contains("Dynamic_"))
{
Response.Write("<p><strong>" + (key.Remove(0,8)).Replace("_"," ") + "</strong> :: " + Request.Form[i] + "</p>");
}
}
}
You need to add a TableCell to the TableRow.Cells collection, and add the TextBox to the TableCell.Controls collection:
TableCell cell = new TableCell();
cell.Controls = new TextBox();
tblRow[j].Cells.Add(cell);
editDataTable.Rows.Add(tblRow[j]);
The most direct way to access the textboxes is to keep them all in a list:
List<TextBox> textBoxes = new List<TextBox>();
and replace cell.Controls = new TextBox(); above with this:
TextBox tb = new TextBox();
textBoxes.Add(tb);
cell.Controls = tb;
And then you can iterate through the textBoxes afterward without having to search for them in a tree.