I am dynamically added textboxes through code behind on button click and it is working fine. On another button click I want to get the values all the dynamically added textbox. But the value of the textboxes is empty. How to retain the value of the dynamically added textbox on postback?
Aspx code
<asp:Table runat="server" id="placeAddTds">
<asp:TableRow>
<asp:TableCell><asp:TextBox ID="txt_from" runat="server" class="sabsw" TabIndex="88"></asp:TextBox></asp:TableCell>
<asp:TableCell><asp:TextBox ID="txt_to" runat="server" class="sabsw" TabIndex="89"></asp:TextBox></asp:TableCell>
<asp:TableCell><asp:TextBox ID="txt_per" runat="server" class="sabsw" TabIndex="90"></asp:TextBox></asp:TableCell>
<asp:TableCell>
<asp:Button class="main-btn add-btn pointer" runat="server" ID="btn_add" Text="Add" OnClick="TdsAddClick" TabIndex="91"></asp:Button>
</asp:TableCell>
</asp:TableRow>
</asp:Table>
Code behind:
protected void TdsAddClick(object sender,EventArgs e)
{
try
{
TextBox[] l1 = new TextBox[txtcount];
TextBox[] l2 = new TextBox[txtcount];
TextBox[] l3 = new TextBox[txtcount];
for (int j = 1; j < txtcount; j++)
{
l1[j] = new TextBox();
l2[j] = new TextBox();
l3[j] = new TextBox();
l1[j].ID = "txt_from" + j;
l1[j].Attributes.Add("class","sabsw");
l2[j].ID = "txt_to" + j;
l2[j].Attributes.Add("class", "sabsw");
l3[j].ID = "txt_per" + j;
l3[j].Attributes.Add("class", "sabsw");
placeAddTds.Rows.Add(new TableRow());
placeAddTds.Rows[j].Cells.Add(new TableCell());
placeAddTds.Rows[j].Cells[0].Controls.Add(l1[j]);
placeAddTds.Rows[j].Cells.Add(new TableCell());
placeAddTds.Rows[j].Cells[1].Controls.Add(l2[j]);
placeAddTds.Rows[j].Cells.Add(new TableCell());
placeAddTds.Rows[j].Cells[2].Controls.Add(l3[j]);
}
txtcount++;
}
catch (Exception ex)
{
}
}
Thanks,
I hope this is his Requirement from your comment.
I have two buttons. On click of one button I want to add textboxes dynamically and on click of the second button I want to save the value of all the textboxes in the database
You have 2 buttons
One button you can create TextBox using Jquery. (Client Side script is very much faster while comparing with postbacks).
Here you can see how to add text box using Jquery
So you can add textbox dynamically without postback.
Another button you can serialize the form using Jquery and send it to server.
Pass Data to Server
It will create a JSON Object and pass it to server.
You can pass in __doPostBack(control,args). In args you can pass the JSON Value.
Click Here
Process the JSON Data
C# JSON custom serialization
This will show how to get data from JSON .
This is an approach which can be implemented with client side scripting and which may works much faster than the method you suggest.
Thanks
You can add a property with ViewState for this in your code-behind and store all values in a list:
private List<string> TextBoxValue
{
get
{
return (List<string>)ViewState["TextBoxValue"];
}
set
{
ViewState["TextBoxValue"] = value;
}
}
You can then store the values of textboxes in this property. example:
TextBoxValue.Add(textBox1.Text);
However, this will make page heavy.
Related
I have two dropdownlist in my project and get the items from sql server. One of them show list of something (And I named it DropdownSoore) and another one show list of members of it (And I named it DropdownAye) that update when select an item from DropdownSoore. then when select an item from DropdownAye go to page of them by forwarding query string. I have no problem to updating DropdownAye but when I select item from it and redirect to page of it, the DropdownAye lose the selected item and show the first item (as default) and index to query string in url.
what should I do?
Excuse me about my grammer, I'm not English...
I set DropdownSoore:
<asp:DropDownList ID="DropDownListSoore" runat="server" class="nav-link btn btn-outline-secondary dropdown-toggle" aria-haspopup="true" aria-expanded="true" AutoPostBack="True"></asp:DropDownList>
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
string IdSoore = Convert.ToString(dt.Rows[i]["IdSoore"]);
string NameSoore = Convert.ToString(dt.Rows[i]["NameSoore"]);
DropDownListSoore.Items.Add(IdSoore + "." + NameSoore);
}
string forwardedIdSoore = Request.QueryString["IdSoore"];
if (forwardedIdSoore != null)
{
DropDownListSoore.Items.FindByValue(forwardedIdSoore);
}
And then set DropdownAye:
<asp:DropDownList ID="DropDownListAye" runat="server" class="nav-link btn btn-outline-secondary dropdown-toggle" aria-haspopup="true" aria-expanded="true" OnSelectedIndexChanged="DropDownListAye_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList>
DropDownListAye.Items.Clear();
for (int i = 0; i <= dt1.Rows.Count - 1; i++)
{
DropDownListAye.Items.Add(Convert.ToString(dt1.Rows[i]["NumberAye"]));
}
int iSelectedAye = Convert.ToInt32(DropDownListAye.SelectedIndex) + 1;
string SelectedAye = Convert.ToString(dt1.Rows[iSelectedAye]["IdAye"]);
Session["SSelectedAye"] = SelectedAye;
string forwardedIdAye = Request.QueryString["IdAye"];
if (forwardedIdSoore != null)
{
DropDownListSoore.Items.FindByValue(forwardedIdAye);
}
in page_Load. and dont have problem to update DopdownAye but when I used it to redirect always get IdAye=1 in url and show the first item of DropdownAye ...:
protected void DropDownListAye_SelectedIndexChanged(object sender, EventArgs e)
{
int SelectedAye = Convert.ToInt32(Session["SSelectedAye"]);
int SelectedSoore = Convert.ToInt32(DropDownListSoore.SelectedIndex) + 1;
string forward = "~/contentAye.aspx?IdSoore=" + SelectedSoore + "&IdAye=" + SelectedAye;
Response.Redirect(forward);
}
I try !IsPostBack but dont have utility too.
I do all thing I think and i dont know what shoild i do.
And where are you loading up these infomration?
Remember, page load ALWAYS fires and triggers for each post-back, for each button click, and any OTHER event on the page that calls code behind.
What does the above really mean?
Well, it means that you need to ONLY ONE TIME load up the drop downs, the gridview(s), or whatever else you have.
So, for the last 200+ web pages I have built, I ALWAYS have this code stub in the page load event:
so this markup:
<h3>Select Hotel</h3>
<asp:DropDownList ID="DropDownList1" runat="server"
DataValueField="ID"
DataTextField="HotelName" Width="356px">
</asp:DropDownList>
<br />
<br />
<asp:Button ID="Button1" runat="server"
Text="Show/get/display drop selection" OnClick="Button1_Click"
CssClass="btn" />
<br />
<h3>Selected result</h3>
<asp:Label ID="Label1" runat="server" Text=""></asp:Label>
and code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// load up data, controls etc.
string strSQL =
"SELECT ID, HotelName FROM tblHotelsA ORDER BY HotelName";
DataTable dt = General.MyRst(strSQL);
DropDownList1.DataSource = dt;
DropDownList1.DataBind();
DropDownList1.Items.Insert(0, new ListItem("Please Select Hotel", ""));
}
}
protected void Button1_Click(object sender, EventArgs e)
{
string sResault =
$#"dropdown list value (pkid) = {DropDownList1.SelectedItem.Value}
<br/>
Dropdown list Text (Hotel) = {DropDownList1.SelectedItem.Text}";
Label1.Text = sResault;
}
and the result:
SUPER important:
If I leave out the !IsPostBack test in page load, then for EVERY button click, the dropdown list re-load code will trigger, and runs BEFORE your button stubb, or post back. As a result, I will see/find/get no valid value from the drop down list, since page load ALWAYS runs before the given code stub, and thus if page load "every time" re-loads the data, then it will blow out the choosen value in the drop down list.
So, loading up of grids, data, dropdowns etc. can ONLY occur one time in page load, since page load ALWAYS runs before each button or event code stub you have behind.
If you thus only load up the controls and dropdowns on the first REAL page load, then the values the user selects in those dropdowns should work, but only will work if you ALWAYS include that all important if (!IsPostBack) code stub in your page load event.
Now, of course in place of that button to loadup + display the dropdown list into that label?
Well, of course that button click may well (like often) jump or navigate to another page. So, in place of that code to fill out the selected value(s) into that lable, we could pass the selected value to the next page. And how to do that? Well, you can use session(), you can use parmaters in the URL, or you can even use a post-back URL, and then the WHOLE previous page becomes available (Page.Previous), which then in theory allows you to grab ANY and ALL values from the previous page.
but, say we choose session() to pass the value, then this:
Session["HotelPK"] = DropDownList1.SelectedItem.Value;
Response.Redirect("GridFun3.aspx");
I'm not quite sure how to solve this problem.
What I'm trying to do:
Go through a gridview with multiple rows, and force the user to enter in specific values if certain textboxes or drop down lists are left on the default value.
What I tried:
Tried client side requirements, by use of the focusout event in javascript. Focus goes onto these fields depending on the previous selections users make, that part works. My code:
var row = $(this).closest('tr');
var text = $(row).find("input[name*='txtCurriculum']").val('enter lesson/unit name here');
var ddl = $(row).find("select[name*='ddlCurrAdditions']");
$(ddl).focusout(function () {
if (ddl.val == "-select curriculum name-") {
$(ddl).focus();
alert("select a curriculum name before moving on");
}
});
$(text).focusout(function () {
if (text.val == 'enter lesson/unit name here') {
$(text).focus();
alert('enter in a lesson or unit name before moving on');
}
});
It never fired properly. the ddl focusout would fire, but wouldn't stop firing until I exited and went back to the page. the text focusout would never fire.
Tried server side validation, with the custom validation feature.
protected void cv1_ServerValidate(object source, ServerValidateEventArgs args)
{
CustomValidator cv = ((CustomValidator)(source));
GridViewRow gvr;
//if (FormCGrid.Rows)
foreach (GridViewRow row in FormCGrid.Rows)
{
//string ddl = ((DropDownList)row.FindControl("ddlCurrAdditions")).SelectedItem.Text;
string txtC = ((TextBox)row.FindControl("txtCurriculum")).Text;
bool ddl = ((DropDownList)row.FindControl("ddlCurrAdditions")).Visible;
if (txtC == "enter lesson/unit name here")
{
args.IsValid = false;
cv.ErrorMessage = string.Format("Please enter the Additional program name on row {0}", row.RowIndex);
}
else if (ddl == true)
{
cv.ErrorMessage = string.Format("Enter your lesson or unit for the curriculum selected on row {0}", row.RowIndex);
args.IsValid = false;
}
}
}
I tried to loop through the rows of the gridview here, checking the named dropdown for visibility (hidden until another dropdownlist reveals it, dependent on the value chosen there) and the textbox for the default value, yet this did not work either. It never fired at all. Any suggestions welcome.
I am generally ok with most programming/coding issues, but I find that working with the asp.net gridview, all of my techniques get thrown out the window.
Ok, there are two ways to do this, and you can use js quite easy here. However, lets go with a 100% server side code. I think sticking to server side code is easier.
Next up? I actually STONG suggest to use a listview in place of a gridview WHEN you plan to edit data. The reason is simple - the ListView does not require those "irratating" "item template" over and over for each asp.net control you want to drop/have in that grid display. Now to be fair, say 2-3 controls - then grid view is ok, but for more, then I really find that the listview starts to win here big time (no need for the item template).
The "trick" with listview is of course to setup a datasoruce on the web page, let it generate the markup, and then blow out (delete) all templates except for the item template. And then delete the datasourc1 item on the page, and remove the datasource1 setting from the list view.
However, lets go with the grid view - this will thus be some what "more messy"
So, we going to fill out the grid - and say we want a city combo box - MUST be selected. And lets toss in a basic check for say LastName also must be entered.
So, we have a city combo box (database driven). And say lets toss in a drop down for a hotel rating (1-5) - but that's only 5 choices - so we have that dropdown with the list in mark-up. (so two combo examples to show how this works).
So, say we have a grid like this, and we hit save, we get this result:
Now, of course we would fancy up that message box area. And in fact, we could VERY easy drop in jQuery.UI and make that cute div on the right appear in a dialog box (and we can trigger that box + jQuery.UI dialog display even from our server side code behind - but lets try and keep this short - but I would fancy up that error message area).
Ok, first the markup:
Just a simple grid, but "a bit messy" due to those "item Templates - (if you not figured this out, I really dislike those template things!!! - and with listview you don't need them!! - just drop in pure clean asp.net controls for the grid). Anyway:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ID">
<Columns>
<asp:BoundField DataField="ID" HeaderText="ID" InsertVisible="False" ReadOnly="True" SortExpression="ID" />
<asp:TemplateField HeaderText="HotelName" >
<ItemTemplate><asp:TextBox id="HotelName" runat="server" Text='<%# Eval("HotelName") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="FirstName" >
<ItemTemplate><asp:TextBox id="FirstName" runat="server" Text='<%# Eval("FirstName") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="LastName" >
<ItemTemplate><asp:TextBox id="LastName" runat="server" Text='<%# Eval("LastName") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="City">
<ItemTemplate>
<asp:DropDownList ID="cboCity" runat="server" DataTextField="City"
DataValueField = "City" Width="110px">
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Active">
<ItemTemplate><asp:CheckBox id="Active" runat="server" Checked = '<%# Eval("Active") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Rating">
<ItemTemplate>
<asp:DropDownList ID="cboRating" runat="server" SelectedValue = '<%# Eval("Rating") %>' >
<asp:ListItem Value="1">Poor</asp:ListItem>
<asp:ListItem Value="2">Just Ok</asp:ListItem>
<asp:ListItem Value="3">Ok</asp:ListItem>
<asp:ListItem Value="4">Good</asp:ListItem>
<asp:ListItem Value="5">Excellent</asp:ListItem>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
<div id="ErrorDialog" style="display:normal;float:left;padding-left:30px;width:20%" runat="server">
<asp:TextBox ID="txtErrors" runat="server" TextMode="MultiLine" Style="width:100%" Rows="20" ></asp:TextBox>
</div>
<div style="clear:both;padding-top:20px">
<asp:Button ID="cmdSave" runat="server" Text="Save" />
<asp:Button ID="cmdUnDo" runat="server" Text="UnDo" Style="margin-left:20px"/>
</div>
Ok, now on SO? Gee, that is about the total max markup I would post in one shot - beyond above, we approach the "not very nice" post territory.
Ok, so lets look at the code to load up this grid. We have this code:
public class EditHotelsG : System.Web.UI.Page
{
private DataTable rstTable = new DataTable();
private DataTable rstCity = new DataTable();
protected void Page_Load(object sender, System.EventArgs e)
{
if (System.Web.UI.Page.IsPostBack == false)
{
ErrorDialog.Visible = false;
LoadGrid();
ViewState["rstTable"] = rstTable;
ViewState["rstCity"] = rstCity;
}
else
{
rstTable = ViewState["rstTable"];
rstCity = ViewState["rstCity"];
}
}
public void LoadGrid()
{
// load up our drop down list from database (city)
string strSQL;
strSQL = "SELECT City from tblCity Order by City";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, new SqlConnection(My.Settings.TEST3)))
{
cmdSQL.Connection.Open();
rstCity.Load(cmdSQL.ExecuteReader);
// now load up main grid view
strSQL = "SELECT ID, FirstName, LastName, HotelName, City, Active, Rating from tblHotels ORDER BY HotelName";
cmdSQL.CommandText = strSQL;
rstTable.Load(cmdSQL.ExecuteReader);
GridView1.DataSource = rstTable;
GridView1.DataBind();
}
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
// set combo box data source
if (e.Row.RowType == DataControlRowType.DataRow)
{
// setup city drop down list
DropDownList cboDrop = e.Row.FindControl("cboCity");
cboDrop.DataSource = rstCity;
cboDrop.DataBind();
cboDrop.Items.Insert(0, new ListItem(string.Empty)); // add blank row
cboDrop.Text = rstTable.Rows(e.Row.RowIndex).Item("City").ToString;
}
}
}
Ok, now that is not all that much code. And a good part was setting up that combo box. Since the cbo box (dropdown list) was database driven, then we have to bind + setup for each row. So, that cost us a good chunk of our coding time.
Note however how we persist the database soruce (table). We do that, since we want to with A LOT LESS code, update the whole database behind in ONE shot. By peristing that table, then we
Transfer data from Grid to table
Save the table to database in one shot
So, this reduces our coding efforts by a truckload.
The code to save our data, thus becomes this gem:
(and note how we ALSO put in our validation code).
protected void cmdSave_Click(object sender, EventArgs e)
{
// pull gride rows back to table.
bool bolDataOk = true;
txtErrors.Text = "";
foreach (GridViewRow rRow in GridView1.Rows)
{
int RecordPtr = rRow.RowIndex;
DataRow OneDataRow;
OneDataRow = rstTable.Rows(RecordPtr);
OneDataRow.Item("HotelName") = rRow.FindControl("HotelName") as TextBox.Text;
OneDataRow.Item("FirstName") = rRow.FindControl("FirstName") as TextBox.Text;
OneDataRow.Item("LastName") = rRow.FindControl("LastName") as TextBox.Text;
OneDataRow.Item("City") = rRow.FindControl("cboCity") as DropDownList.Text;
OneDataRow.Item("Active") = rRow.FindControl("Active") as CheckBox.Checked;
OneDataRow.Item("Rating") = rRow.FindControl("cboRating") as DropDownList.SelectedValue;
// check for missing City, or LastName
if (IsDBNull(OneDataRow("City")) | OneDataRow("City") == "")
{
bolDataOk = false;
if (txtErrors.Text != "")
txtErrors.Text += Constants.vbCrLf;
txtErrors.Text += "Missing City on row = " + (rRow.RowIndex + 1).ToString;
}
if (IsDBNull(OneDataRow("LastName")) | OneDataRow("LastName") == "")
{
bolDataOk = false;
if (txtErrors.Text != "")
txtErrors.Text += Constants.vbCrLf;
txtErrors.Text += "Missing lastName on row = " + (rRow.RowIndex + 1).ToString;
}
}
ErrorDialog.Visible = !(bolDataOk);
// now send table back to database with updates
if (bolDataOk)
{
string strSQL = "SELECT ID, FirstName, LastName, City, HotelName, Active, Rating from tblHotels WHERE ID = 0";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, new SqlConnection(My.Settings.TEST3)))
{
cmdSQL.Connection.Open();
SqlDataAdapter daupdate = new SqlDataAdapter(cmdSQL);
SqlCommandBuilder cmdBuild = new SqlCommandBuilder(daupdate);
daupdate.Update(rstTable);
}
}
}
Now, once again, that above code snip is a wee bit long - and is aobut the max size of a code snip for SO. I would perhaps consider breaking out that above last code into 2 or even 3 routines.
but, all in all, the above shows quite a few tricks, and approaches here.
As noted, I was going to post how you can/could verify using client side code, ,but the OVER ALL idea of saving back the whole grid with one database update? Well, I would use that trick with a listview, gridview, and even if I was doing client side js verification? I will still use the bulk of the above design pattern.
And the un-do button? Well, I never finished that part - but it would just call the same code as the first page load (postback - false, and you would simply re-bind the grid from the database source for the un-do button.
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.
I have an ASP table, with a dropdownlist and text box in its 1st row. I have a button on click of which a similar row should be added. I have the following function in its button click event.
public void addRow()
{
int flag = Table1.Rows.Count;
DropDownList ddl = new DropDownList();
ddl.ID = "ddl" + (flag + 1).ToString();
TextBox tBox = new TextBox();
tBox.ID = "txt" + (flag + 1).ToString();
TableCell tCell1 = new TableCell();
TableCell tCell2 = new TableCell();
tCell1.Controls.Add(ddl);
tCell2.Controls.Add(tBox);
TableRow tRow = new TableRow();
tRow.Cells.Add(tCell1);
tRow.Cells.Add(tCell2);
Table1.Rows.Add(tRow);
}
This code works fine when adding 2nd row. When I click the button again, the value of flag which should have the value of number of rows of the table is not getting incremented. So a new row doesn't get created. Can you please tell me why the value of flag doesn't get incremented ? Or is their an easier way to do this ? Direct answer or link would be helpful.
Mark up code for Table:
<asp:Table ID="Table1" runat="server" Width="410px">
<asp:TableRow>
<asp:TableCell>
<asp:DropDownList runat="server" ID="ddl1"></asp:DropDownList>
</asp:TableCell>
<asp:TableCell>
<asp:TextBox runat="server" ID="txt1"></asp:TextBox>
</asp:TableCell>
</asp:TableRow>
</asp:Table>
Warning - Big ugly block of text
I'm going to try and explain why what you're trying to do is not as simple as it sounds (or as simple as I'd like it to be) and hopfully give you an alterantive approach, which while it
may not be straight forward, it should be "better".
It looks a high level overview of what you are trying to do is the following:
Give the user the oppotunity to add one or more records to a collection (database) before finalising the save.
It is possible to do this using postback to generate the additional rows/items beofore clicking the final save button. Hover this may not be the easiest, or best, way to do this.
From a technical viewpoint adding dynamic controls has to be handeld in a very specific manner at a specific point in the ASP.net page lifecylce. Dynamic controls also have to be recreated on every post back, so every time a user clicks the "add" button, you will have to re-reate the rows you already have in addition to the new row. This is why you only get the one row in this example. Thats just the adding of the dynaimc controls. Wait till you get to getting data from your dynamic text boxes and drop downs!
From an end users perspective, this approach is also not ideal as every time the user clicks the "add" button the page cycles through the request/response process, which at the minumun cuases a bit of an ugly flash.
Update
But how? I hear you ask. Well here it is (I've simplified it to one column for this example):
ASPX
<asp:Table ID="Table1" runat="server">
<asp:TableRow>
<asp:TableCell><asp:TextBox ID="txt1" runat="server"> </asp:TextBox></asp:TableCell>
</asp:TableRow>
</asp:Table>
<asp:Button ID="btnAdd" runat="server" Text="Add" onclick="Button1_Click" />
<asp:HiddenField ID="hdnRowCount" runat="server" />
<asp:Button ID="btnSave" runat="server" onclick="btnSave_Click" Text="Save" />
<div>
<asp:Label ID="lblResult" runat="server" Visible="false"></asp:Label>
</div>
C#
private int rowCount = 1;
private bool rowNeeded = false;
protected override void OnPreInit(EventArgs e)
{
//Pre-emptively create additional row on post back.
//We'll remove it if we don't need it later
//Controls Have to be added at pre-init to maintain their view state
//Adding a row in the button click event will result in any data
//entered in that row dissapearing from viewstate on next post back
if (IsPostBack)
{
//Get number of additional rows from hidden field
//We're using a hidden form field and Request.Form
//instead of ViewsState and hdnRowCount.value
//because ViewState has not been loaded at this stage
//of the page life cycle
if (!string.IsNullOrEmpty(Request.Form["hdnRowCount"]))
{
rowCount = int.Parse(Request.Form["hdnRowCount"]);
}
for (int i = 0; i < rowCount; i++)
{
TableRow tr = new TableRow();
TableCell tc = new TableCell();
TextBox tb = new TextBox();
// +2 as there are existing controls with "1", eg txt1
tb.ID = string.Format("txt{0}",i+2);
tc.Controls.Add(tb);
tr.Cells.Add(tc);
Table1.Rows.Add(tr);
}
}
base.OnPreInit(e);
}
//Add Row Click
protected void Button1_Click(object sender, EventArgs e)
{
//Hold number of additional rows in hidne field
hdnRowCount.Value = (++rowCount).ToString();
//Let the page know we need to keep the row
rowNeeded = true;
}
//An Exmple of how to get your data out
protected void btnSave_Click(object sender, EventArgs e)
{
string s = "";
//Iterate through the table rows finding the controls
//Using count -1 as we still have the pre-emptive row
for(int i = 0; i < Table1.Rows.Count-1; i++)
{
TextBox tb = (TextBox)Table1.Rows[i].FindControl(string.Format("txt{0}",i+1));
s += "," + tb.Text;
}
lblResult.Text = s;
lblResult.Visible = true;
}
protected override void OnLoadComplete(EventArgs e)
{
//Get rid of pre-emptive row if we don't need it
if (IsPostBack)
{
if (!rowNeeded)
{
Table1.Rows.RemoveAt(Table1.Rows.Count - 1);
}
}
base.OnLoadComplete(e);
}
End Update
So, how would I do it? Client side, is the answer to that question. I would use jQuery to create a clone of the first row. You then would need to adjust the id and name attributes of your form fields before appending your new row to the table. You would also need a hidden form field to keep track of the number of hidden rows. When your user finaly hits the "save" button, on the C# side, get the number of additional rows from you hidden field the go old school and use Request.Form[] to get the value of each of your added fields.
This, in my opionion, will be a smoother experience for the end user.
Another, somewhat hacky option,if you have a defined maximum number of additional rows. For this have the mamimum rows, with form controls in the table. You can then either use visible="false" in the aspx and use post back to change that to visible="true" when the user clicks "add". The other option there is to give the row a CSS class that hides the row and then use javascript/jquery to remove the class to make the row visible.
The issue here is that you are not understanding the ASP.Net Page Lifecycle and more importantly how it interacts with dynamic controls (your added controls.)
Unfortunately, we can not answer this question without understanding whether the values in these "dynamically" created controls are used server-side or client-side.
I can guess that:
1) If they are for client side consumption/usage, you are doing wrong. If you need more info ask another (more specific) question.
2) If they are for server side consumption/usage, you must only allow one new record per postback, and should include a "Save" button that will save the current "new" row and cause a page refresh that the user can add a new entry to.
3) See rule #0 (Every rule has exceptions. {This rule applies in so far that you understand the problem at hand.})
Monk
I have the following scenario:
My page has a dropdown list that allows user to choose a category.
For each category, there is a set of attributes, whose values should be fetched from the user. The number of attributes are different for each category.
Depending on the category the user chooses, a set of dropdown lists should be created corresponding to the attributes and filled with corresponding attribute values.
Since it is required that the page should not reload, I plan to fetch the data (from SQL Server 2008) using AJAX (?). I'm new to ASP.NET and have not used AJAX though I'm comfortable with C#. Need advice on how to proceed.
EDIT: Is this useful if I need to dynamically generate combo boxes?
You can use UpdatePanel or PageMethods
in both cases and in any case, I would say, you do need to know JavaScript when you want to use AJAX and make dynamic web applications. It take some time but it pays off don't worry.
you can search here in SO about PageMethod, for example see this one:
Regarding PageMethod in asp.net
You could use the following approach (if you do not feel comfortable building a more complex UI with javascript).
It works by dynamically creating the attribute DropDownLists when the page loads (you would implement it based on the result of a DB query) and hiding each one, ready for display later on.
Upon the selection of a category the correct DropDownLists would then be made visible (again a query here could determine which attribute DropDownLists become visible).
Obviously it will require some modifications to probably generate a Panel which contains each DropDownList and a Label control, instead of just creating a number of DropDownLists.
You would then show/hide the Panel instead of the DropDownList, but hopefully it points you in the right direction.
Hope this helps.
Markup
<style type="text/css">
select
{
display:block;
margin-top:10px;
}
</style>
....
<asp:ScriptManager ID="scriptManager" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="updatePanel" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<!-- Category selection -->
<asp:DropDownList ID="categoryDropDownList" runat="server" AutoPostBack="true" OnSelectedIndexChanged="categoryDropDownList_SelectedIndexChanged">
<asp:ListItem Text="Please select a category" Value="0"></asp:ListItem>
</asp:DropDownList>
<br />
<!-- Used to store the drop downs -->
<asp:Panel ID="dropDownContainer" runat="server"></asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
Code
protected void Page_Load(object sender, EventArgs e)
{
LoadDropDownLists();
}
private void LoadDropDownLists()
{
//Dummy data, you would pull your categories/attributes from whichever datasource
//you are using
var categories = new[]{
new { Name = "Category 1", Id = 1, Attributes = new string[]{"GA", "FA", "RA"} },
new { Name = "Category 2", Id = 2, Attributes = new string[]{"GA", "NA"} }
};
//Loop through the categories, load the dropdown
foreach (var category in categories)
{
if (!IsPostBack)
categoryDropDownList.Items.Add(new ListItem(category.Name, category.Id.ToString()));
//For each attribute create a drop down and populate with data as required
foreach (var attribute in category.Attributes)
{
string dropDownListId = FormatDropDownListId(attribute);
if (!DropDownListExists(dropDownListId))
{
DropDownList dropDownList = new DropDownList();
dropDownList.ID = dropDownListId;
dropDownList.Visible = false;
dropDownList.Items.Add(new ListItem(attribute));
dropDownContainer.Controls.Add(dropDownList);
}
}
}
}
private bool DropDownListExists(string id)
{
DropDownList dropDownList = (DropDownList)dropDownContainer.FindControl(id);
return dropDownList != null;
}
protected void categoryDropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
//Reset all visible dropdowns
HideAllDropDownLists();
//Get the selected category
string selectedItem = categoryDropDownList.SelectedItem.Value;
switch (selectedItem)
{
case "1":
{
//Here you would connect to db and pull correct attributes
//then set the visible dropdowns as required
SetDropDownVisibility(FormatDropDownListId("GA"));
SetDropDownVisibility(FormatDropDownListId("FA"));
SetDropDownVisibility(FormatDropDownListId("RA"));
} break;
case "2":
{
SetDropDownVisibility(FormatDropDownListId("GA"));
SetDropDownVisibility(FormatDropDownListId("NA"));
} break;
}
}
private void SetDropDownVisibility(string id)
{
DropDownList dropDownList = (DropDownList)dropDownContainer.FindControl(id);
if(dropDownList != null)
dropDownList.Visible = true;
}
private void HideAllDropDownLists()
{
foreach (Control control in dropDownContainer.Controls)
{
control.Visible = false;
}
}
private string FormatDropDownListId(string id)
{
return string.Format("dropDown{0}", id);
}
If you're using ASP.NET webforms then I don't believe you need to use AJAX or JavaScript.
I would do the following
Set autopostback = true on your combobox
Add an event handler for the OnChanged event of the combobox
Inside the event handler, apply rules at to load / generate / populate child comboboxes
Add those combo boxes to the form
You can either hide the comboboxes (as I see in #jdavies answer), or start without any and dynamically create & add them to the form.
This question deals with the same issue:
DropDownList and Update Panel