I'm making a database manager program and the program creates only as much textbox
as needed (counts the columns in the table). But for some reason the program is not removing all the textboxes I want.
New textboxes should be created and old ones should be removed on every "ComboBoxSelectedIndexChange".
Here is my code snippet:
var elm = panel20.Controls.OfType<System.Windows.Forms.TextBox>();
foreach (var item in elm)
{
DeleteControlsWithTag(item.Tag.ToString());
}
button19.Enabled = true;
one_cond = string.Empty;
if (comboBox19.Text != string.Empty)
{
one_cond = comboBox19.Text.ToUpper();
if (one_cond == "ÖSSZES")
{
string allcolumn = "(";
colnames.Remove("Összes");
for (int i = 0; i < colnames.Count - 1; i++)
{
allcolumn += colnames.ElementAt(i) + ",";
}
allcolumn += colnames.Last() + ")";
button19.Enabled = false;
textBox16.Text = ManualQuery + " " + from + " " + allcolumn + " " + "VALUES" + " ";
if (3 < colnames.Count)
{
for (int i = 0; i < colnames.Count; i++)
{
System.Windows.Forms.TextBox textbox = new System.Windows.Forms.TextBox();
textbox.Name = $"textbox_{i}";
textbox.AccessibleName = $"textbox_{i}";
textbox.Tag = $"textbox_{i}";
panel20.Controls.Add(textbox);
textbox.Parent = panel20;
//"textbox_" + colnames[i] + i
if (2<i)
{
if (i == 3)
{
textbox.Location = new System.Drawing.Point(489, 49);
}
else
{
textbox.Location = new System.Drawing.Point(489 + ((i - 3) * 71), 49);
}
}
else
{
if (0 < i)
{
textbox.Location = new System.Drawing.Point(467 + (i * 71), 17);
}
else
{
textbox.Location = new System.Drawing.Point(467, 17);
}
}
textbox.Size = new System.Drawing.Size(64, 20);
}
}
else
{
for (int i = 0; i < colnames.Count; i++)
{
System.Windows.Forms.TextBox textbox = new System.Windows.Forms.TextBox();
textbox.Name = $"textbox_{i}";
textbox.AccessibleName = $"textbox_{i}";
textbox.Parent = panel20;
panel20.Controls.Add(textbox);
textbox.Tag = $"textbox_{i}";
if (0<i)
{
textbox.Location = new System.Drawing.Point(467 + (i*71), 17);
}
else
{
textbox.Location = new System.Drawing.Point(467, 17);
}
textbox.Size = new System.Drawing.Size(64, 20);
}
}
}
else
{
onecondinsert = "(" + one_cond + ")";
textBox16.Text = ManualQuery + " " + from + " " + onecondinsert + " " + "VALUES" + " ";
System.Windows.Forms.TextBox textbox = new System.Windows.Forms.TextBox();
textbox.Name = $"textbox";
textbox.AccessibleName = $"textbox";
textbox.Location = new System.Drawing.Point(464, 20);
textbox.Size = new System.Drawing.Size(100, 20);
textbox.Parent = panel20;
textbox.Tag = "textbox";
panel20.Controls.Add(textbox);
//tbox.TextChanged += tbox_TextChanged;
}
}
MessageBox.Show(elm.Count().ToString());
}
and here is some screenshot of how the problem looks like
on the first select it works
on this picture you can see the program did not delete all the text box.
longer textbox is the new one
On the third picture I selected another column (only one textbox should be visible) but the program deletes one textbox
as you can see here
After the third selection it deletes the last extra textbox too
3. try it "works"
I have tried different ways to delete from the panel20.controls
but all produced the same bug
Hope someone can help me, Thanks
Update:
I tried another method from here:
C#, deleting all the controls with the same .Tag
private List<Control> GetTaggedControls(string tag, Control parent)
{
var taggedControls = new List<Control>();
foreach (Control control in parent.Controls)
{
if (control.Tag?.ToString() == tag)
{
taggedControls.Add(control);
}
// Recursively call this method in case this is a container
taggedControls.AddRange(GetTaggedControls(tag, control));
}
return taggedControls;
}
// Deletes all controls with the specified tag
private void DeleteControlsWithTag(string tag)
{
foreach (Control control in GetTaggedControls(tag, this))
{
panel20.Controls.Remove(control);
}
}
and I tried to delete the textboxes like this:
var elm = panel20.Controls.OfType<System.Windows.Forms.TextBox>();
foreach (var item in elm)
{
DeleteControlsWithTag(item.Tag.ToString());
}
but it is still not working
Your GetTaggedControls() function is finding ANY type of control, RECURSIVELY starting at the main Form container level, that has a specific tag. So returned controls might not be from many containers, but your DeleteControlsWithTag() method tries to remove all matches only from panel20. That may or may not be a problem; it's unclear from the pictures and possibly incomplete posted code.
When you say remove the old textboxes, can we simply remove ALL existing TextBoxes, or do you really need to match tags?
From your original attempt:
var elm = panel20.Controls.OfType<System.Windows.Forms.TextBox>();
foreach (var item in elm)
{
DeleteControlsWithTag(item.Tag.ToString());
}
This is finding ALL textboxes in panel20, then trying to delete them based on matching tags with the helper function. So the tag doesn't even matter since you are finding all textboxes to begin with...
Just try removing all TextBoxes from panel20?
var elm = panel20.Controls.OfType<System.Windows.Forms.TextBox>().ToList();
elm.ForEach(tb => panel20.Controls.Remove(tb));
Okay, now I tried to tie together Idle_Mind's and LarsTech's recommendation
and finally it's working fine with "Dispose".
Thanks guys for your quick help.
here is the previous code:
var elm = panel20.Controls.OfType<System.Windows.Forms.TextBox>();
foreach (var item in elm)
{
DeleteControlsWithTag(item.Tag.ToString());
}
and here is the new one:
var elm = panel20.Controls.OfType<System.Windows.Forms.TextBox>().ToList();
elm.ForEach(tb => tb.Dispose());
I have a little problem. First of all, some info:
- On my page, I have an UpdatePanel with a button inside it.
- When you click this button, I generate a new row with dropdown lists. Each time I have to generate a table from scratch, because it resets after the click, so I update [ViewState] value and generate as many rows as clicks.
- Outside the panel, I have another button. After clicking this button, I want to collect data from drop-down lists. To do it, I have to get to these controls.
I tried to use function FindControl(), but I guess I can't - as far as I know, it does not perform a deep search. This means I have to pass as a parameter the exact container with this control. Because control is inside the table, I should get to the <td> value and I can't do that (<td> does not have ID - yes, I can add it but <td> is also dynamically created. That means I would need to get first to <td>, then to my control (guess what - <tr> is also created dynamically).
Because I can't use FindControl function, I use FindRecursiveControl function (code below) The problem is, that this function neither finds anything. Any suggestions about what might be the reason? I added this whole info in case that the reason is for example usage of UpdatePanel and page life cycle.
private Control FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID)
{
return rootControl;
}
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn =
FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null)
{
return controlToReturn;
}
}
return null;
}
My usage of this function:
string control_id = "parametr" + i;
DropDownList dropdown = (DropDownList)FindControlRecursive(UpdatePanel1, control_id);
Script generating table in UpdatePanel after button click
protected void generuj_tabele(int il_klik)
{
il_par.Text = "Ilość parametrów: " + il_klik.ToString();
TableRow table_head = new TableRow();
table_head.Attributes.Add("class", "w3-green");
Table1.Rows.Add(table_head);
for (int j = 0; j < 5; j++)
{
TableCell cell = new TableCell();
table_head.Cells.Add(cell);
}
Table1.Rows[0].Cells[0].Text = "Parametr";
Table1.Rows[0].Cells[1].Text = "Wartość początkowa";
Table1.Rows[0].Cells[2].Text = "Inkrementacja?";
Table1.Rows[0].Cells[3].Text = "Zwiększ o:";
Table1.Rows[0].Cells[4].Text = "Zwiększ co:";
RootObject obj = (RootObject)Session["get_offer"];
for (int i = 0; i < il_klik; i++)
{
parametr = new DropDownList();
wartosc = new TextBox();
inkrementacja = new CheckBox();
inkrementacja_numer = new TextBox();
skok = new TextBox();
//inkrementacja_numer.Enabled = false;
// skok.Enabled = false;
inkrementacja_numer.Attributes.Add("Type", "number");
skok.Attributes.Add("Type", "number");
//inkrementacja.CheckedChanged += new EventHandler((s, eventarg) => checkbox_change(s, eventarg, i));
//inkrementacja.AutoPostBack = true;
//parametr.AutoPostBack = true;
TableRow row = new TableRow();
Table1.Rows.Add(row);
parametr.EnableViewState = true;
wartosc.EnableViewState = true;
inkrementacja.EnableViewState = true;
inkrementacja_numer.EnableViewState = true;
skok.EnableViewState = true;
for (int j = 0; j < 5; j++)
{
TableCell cell = new TableCell();
row.Cells.Add(cell);
}
Table1.Rows[i + 1].Cells[0].Controls.Add(parametr);
Table1.Rows[i + 1].Cells[1].Controls.Add(wartosc);
Table1.Rows[i + 1].Cells[2].Controls.Add(inkrementacja);
Table1.Rows[i + 1].Cells[3].Controls.Add(inkrementacja_numer);
Table1.Rows[i + 1].Cells[4].Controls.Add(skok);
if (i == il_klik - 1)
{
wystaw_liste(obj);
Price pr = obj.sellingMode.price;
parametr.Items.Add(pr.amount.ToString());
List<Parameter> par = obj.parameters;
foreach (Parameter p in par)
{
List<string> val = p.values;
if (val.Count() > 0)
{
foreach (string v in val)
{
parametr.Items.Add(v);
}
}
}
foreach (string p in parametry_list)
{
parametr.Items.Add(p);
}
parametry_list.Clear();
}
parametry.Add(parametr);
wartosci.Add(wartosc);
inkrementacje.Add(inkrementacja);
inkrementacje_numery.Add(inkrementacja_numer);
skoki.Add(skok);
if (i == il_klik - 1)
{
Session["v_parametr"] = parametry;
Session["v_wartosc"] = wartosci;
Session["v_inkrementacja"] = inkrementacje;
Session["v_ink_nr"] = inkrementacje_numery;
Session["v_skok"] = skoki;
}
parametr.ID = "parametr" + i;
wartosc.ID = "wartosc" + i;
inkrementacja.ID = "inkrementacja" + i;
inkrementacja_numer.ID = "inkrementacja_numer" + i;
skok.ID = "skok" + i;
}
}
When I try to check parameters of DropDownList (e.g. SelectedValue) I get error "Object reference not set to an instance of an object"
I am trying to make a timetable using a .txt file as a source, but I am having some trouble. I am creating a website, using C# and ASP.NET.
And this is what I get:
I tried so hard to put all the data in the same 5 rows, but it's just seems impossible, they always hop to the different rows. Just to make things clear, this is my expectation:
Just in case, this is the code I wrote, maybe I messed up somewhere:
string[] allLines = File.ReadAllLines(Server.MapPath("App_Data/Classes.txt"))
foreach(string line in allLines){
string[] parts = line.Split(',');
TableRow row = new TableRow();
TableCell number = new TableCell();
TableCell lesson = new TableCell();
TableCell number2 = new TableCell();
TableCell lesson2 = new TableCell();
TableCell number3 = new TableCell();
TableCell lesson3 = new TableCell();
TableCell number4 = new TableCell();
TableCell lesson4 = new TableCell();
TableCell number5 = new TableCell();
TableCell lesson5 = new TableCell();
if (parts[1] == "Monday" & parts[0] == "5a klase")
{
number.Text = parts[2];
lesson.Text = parts[3];
}
if (parts[1] == "Tuesday" & parts[0] == "5a klase")
{
number2.Text = parts[2];
lesson2.Text = parts[3];
}
if (parts[1] == "Wednesday" & parts[0] == "5a klase")
{
number3.Text = parts[2];
lesson3.Text = parts[3];
}
if (parts[1] == "Thursday" & parts[0] == "5a klase")
{
number4.Text = parts[2];
lesson4.Text = parts[3];
}
if (parts[1] == "Friday" & parts[0] == "5a klase")
{
number5.Text = parts[2];
lesson5.Text = parts[3];
}
row.Cells.Add(number);
row.Cells.Add(lesson);
row.Cells.Add(number2);
row.Cells.Add(lesson2);
row.Cells.Add(number3);
row.Cells.Add(lesson3);
row.Cells.Add(number4);
row.Cells.Add(lesson4);
row.Cells.Add(number5);
row.Cells.Add(lesson5);
Table1.Rows.Add(row);
}
Any help is much appreciated!
P.S. Here's how the .txt file looks like (ignore the non-English names, those are school subjects in Lithuanian):
So here's something to start from. I just freehanded it, so I'm sure there's syntax and other errors in there, but try it, change your own code, etc
public class DoWork(){
var scheduleEntries = new List<ScheduleEntry>();
File.ReadAllLines(Server.MapPath("App_Data/Classes.txt")).Select(i=>scheduleEntries.Add(new ScheduleEntry(i)));
var maxRowCount = scheduleEntries.Max(i=>i.Ordinal);
for (int k = 1; k <= maxRowCount; k++){
var tableRow = new TableRow;
// Monday
var mondayCellItem = scheduleEntries.FirstOrDefault(i=>i.Ordinal == k && i.Day == "Monday")
var mondayCell = new TableCell{
Text = $"{mondayCellItem.Class}"
// Tuesday, etc
tableRow.Cells.Add(mondayCell);
tableRow.Cells.Add(tuesdayCell);
//etc
}
}
internal class ScheduleEntry(){
public string Day {get;set;}
public int Ordinal {get;set;}
public string ClassName {get;set;}
public ScheduleEntry(string inRow){
var values = inRow.Split(',');
Day = values[1]; // should do some validation here
Ordinal = int.Parse(values[2]); // and here
ClassName = values[3]; // and here
}
}
}
You can use Linq to split the text file into rows which are already split by the comma. Then it's just a matter of looping all the items. It all works dynamically so you can add as much weekdays and rows per weekday as needed.
//create a list with the rows already split by comma
List<string[]> allRows = File.ReadLines(Server.MapPath("/textfile1.txt")).Select(line => line.Split(',')).ToList();
//group by weekday
var sortedList = allRows.GroupBy(x => x[1]).ToList();
//get the max number of rows needed
var rowCount = sortedList.Max(x => x.Count()) + 1;
//create a new table
Table table = new Table();
//fill the table with rows and columns
for (int i = 0; i < rowCount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < (sortedList.Count * 2); j++)
{
//if it is the first row add the row headers
if (i == 0)
{
row.BackColor = Color.LightGray;
if (j % 2 == 1)
row.Cells.Add(new TableCell() { Text = sortedList[j /2].Key });
else
row.Cells.Add(new TableCell() { Text = "Nr" });
}
else
{
row.Cells.Add(new TableCell());
}
}
table.Rows.Add(row);
}
//loop all the weekdays
for (int i = 0; i < sortedList.Count; i++)
{
int j = 1;
//loop all the items within a weekday
foreach (var item in sortedList[i])
{
//the item[x] is based on the sample txt file, it is the row split by comma index
table.Rows[j].Cells[i * 2].Text = item[2];
table.Rows[j].Cells[(i * 2) + 1].Text = item[3];
j++;
}
}
I am creating an application where different kinds of survey questions are pulled from a database.
The first kind I am implementing is a list of questions each with four radio buttons to choose from.
My code for creating the radio button lists and mark up dynamically is
protected void ProcessData(DataTable question, DataTable answers)
{
int i = 0;
foreach (DataRow row in question.Rows)
{
HtmlGenericControl rowDiv = new HtmlGenericControl("div");
rowDiv.Attributes["class"] = "classRowDiv";
panel1.Controls.Add(rowDiv);
HtmlGenericControl questionDiv = new HtmlGenericControl("div");
questionDiv.Attributes["class"] = "classQuestionDiv";
questionDiv.ID = "questionDiv" + i.ToString();
rowDiv.Controls.Add(questionDiv);
Label questionLabel = new Label();
questionLabel.Attributes["class"] = "classQuestionLabelDiv";
if (row[2].ToString() == "Radio button")
{
questionLabel.Text = row[3].ToString();
rowDiv.Controls.Add(questionLabel);
HtmlGenericControl radioDiv = new HtmlGenericControl("div");
radioDiv.Attributes["class"] = "classRadioDiv";
radioDiv.ID = "radioDiv" + i.ToString();
rowDiv.Controls.Add(radioDiv);
RadioButtonList rbl = new RadioButtonList();
rbl.ID = "List" + i.ToString();
radioDiv.Controls.Add(rbl);
rbl.RepeatDirection = RepeatDirection.Horizontal;
Label lbl = new Label();
lbl.Text = "<hr />";
panel1.Controls.Add(lbl);
DataRow rowA;
for (int j = 0; j < 4; j++ )
{
rowA = answers.Rows[j];
ListItem button = new ListItem(rowA[2].ToString(), j.ToString());
button.Attributes["class"] = "radioStyle";
rbl.Items.Add(button);
}
i++;
}
}
}
The problem I am having is actually getting the selected values of the buttons. I have come to understand from reading around that dynamically created controls must be re-loaded with the same ID after a postback but I do not fully understand how to achieve this as the amount of radio buttons created is dependent on the result set from the database.
I have a div Conatining a Panel in aspx page
<div id="divNameofParticipants" runat="server">
<asp:Panel ID="panelNameofParticipants" runat="server">
</asp:Panel>
</div>
I am populating the panel dynamically from codebehind with the following code:
void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
StringBuilder sbparticipantName=new StringBuilder();
try
{
int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue);
ViewState["numberofparticipants"] = numberofparticipants;
Table tableparticipantName = new Table();
int rowcount = 1;
int columnCount = numberofparticipants;
for (int i = 0; i < rowcount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < columnCount; j++)
{
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
cell.ID = "cell" + Convert.ToString(i);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
}
tableparticipantName.Rows.Add(row);
panelNameofParticipants.Controls.Add(tableparticipantName);
}
}
catch(Exception ex)
{
}
}
Now I want to access the value of these dynamically generated textbox in the codebehind.for which i my code is as under:
public void CreateControls()
{
try
{
//String test1 = test.Value;
List<string> listParticipantName = new List<string>();
if (ViewState["numberofparticipants"] != null)
{
int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
for (int i = 0; i < numberofparticipants; i++)
{
string findcontrol = "txtNameofParticipant" + i;
TextBox txtParticipantName = (TextBox)panelNameofParticipants.FindControl(findcontrol);
listParticipantName.Add(txtParticipantName.Text);
}
}
}
catch (Exception ex)
{
}
}
but I am not able to get the values in codebehind.
TextBox txtParticipantName = (TextBox)panelNameofParticipants.FindControl(findcontrol);
the above code is not able to find the control and its always giving null.what am i doing wrong.i recreated the controls in page load also since postback is stateless but still no success.
Thanks in Advance
You need to create dynamic controls in PreInit not in OnLoad. See documentation here: https://msdn.microsoft.com/en-us/library/ms178472.aspx
Re/Creating the controls on page load will cause the ViewState not to be bound to the controls because viewstate binding happens before OnLoad.
As mentioned by other people. To create dynamic controls your need to do this for every postback and at the right time. To render dynamic controls, use the Preinit event. I suggest that your have a look at https://msdn.microsoft.com/en-us/library/ms178472(v=vs.80).aspx to learn more about this.
MSDN - PreInit:
Use this event for the following:
Check the IsPostBack property to determine whether this is the first time the page is being processed.
Create or re-create dynamic controls.
Set a master page dynamically.
Set the Theme property dynamically.
Read or set profile property values.
NoteNote If the request is a postback, the values of the controls have not yet been restored from view state. If you set a control property at this stage, its value might be overwritten in the next event.
The next interesting event is Preload that states:
MSDN - PreLoad
Use this event if you need to perform processing on your page or control before the Load event.
After the Page raises this event, it loads view state for itself and all controls, and then processes any postback data included with the Request instance.
Meaning that in the next event Load (Page_Load) the viewstate should be loaded, so here you should effectively be able to check your values.
You also need to make sure that view state is enabled and the easiest is probably in the page level directive:
<%#Page EnableViewState="True" %>
Take a look at the article https://msdn.microsoft.com/en-us/library/ms972976.aspx2 that goes more into the depth of all this
Note
If your problem is that you need to create controls dynamically on a button click, and there will be many controls created, you should probably turn to jQuery ajax and use the attribute [WebMethod] on a public function in your code behind. Creating dynamic controls and maintaining the ViewState is quite costly, so i really recommend this, for a better user experience.
If you use a DataPresentation control like asp:GridView it will be much easier.
Markup
<asp:GridView ID="ParticipantsGrid" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:TemplateField HeaderText="Participants">
<ItemTemplate>
<asp:TextBox ID="txtNameofParticipant" runat="server"
Text='<%# Container.DataItem %>'>
</asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code-behind
protected void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
try
{
var selectedParticipantCount = Convert.ToInt32(drpNoofparticipants.SelectedValue);
var items = Enumerable.Repeat(string.Empty, selectedParticipantCount).ToList();
ParticipantsGrid.DataSource = items;
ParticipantsGrid.DataBind();
}
catch (Exception ex)
{ }
}
public void CreateControls()
{
try
{
var participants = ParticipantsGrid.Rows
.Cast<GridViewRow>()
.Select(row => ((TextBox)row.FindControl("txtNameofParticipant")).Text)
.ToList();
}
catch (Exception ex)
{ }
}
I think you are doing that correctly. In asp.net webforms, there are lots of times that you will experienced strange things that are happening. Especially with the presence of ViewState. First we need to troubleshoot our code and try different tests and approaches. That will be my advise for you, pleas use this code and debug if what is really happening:
void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
StringBuilder sbparticipantName=new StringBuilder();
try
{
int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue);
ViewState["numberofparticipants"] = numberofparticipants;
Table tableparticipantName = new Table();
int rowcount = 1;
int columnCount = numberofparticipants;
TableRow row = new TableRow();
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "NameTest";
txtNameofParticipant.Text = "This is a textbox";
cell.ID = "cellTest";
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
tableparticipantName.Rows.Add(row);
panelNameofParticipants.Controls.Add(tableparticipantName);
}
catch(Exception ex)
{ // please put a breakpoint here, so that we'll know if something occurs,
}
}
public void CreateControls()
{
try
{
//String test1 = test.Value;
List<string> listParticipantName = new List<string>();
//if (ViewState["numberofparticipants"] != null)
//{
string findcontrol = "NameTest";
TextBox txtParticipantName = (TextBox)panelNameofParticipants.Controls["NameText"];
//check if get what we want.
listParticipantName.Add(txtParticipantName.Text);
//}
}
catch (Exception ex)
{// please put a breakpoint here, so that we'll know if something occurs,
}
}
Dynamic controls need to be recreated on every post-back. The easiest way to achieve that in your code would be to cache the control structure and repopulate it. This how it can be done:
void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
StringBuilder sbparticipantName=new StringBuilder();
Panel p1 = new Panel();
try
{
int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue);
ViewState["numberofparticipants"] = numberofparticipants;
Table tableparticipantName = new Table();
int rowcount = 1;
int columnCount = numberofparticipants;
for (int i = 0; i < rowcount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < columnCount; j++)
{
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
cell.ID = "cell" + Convert.ToString(i);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
}
tableparticipantName.Rows.Add(row);
p1.Controls.Add(tableparticipantName);
}
Cache["TempPanel"] = p1;
panelNameofParticipants.Controls.Add(p1);
}
catch(Exception ex)
{
}
}
Look for the p1 panel in the above code to see the changes. Now the code for the CreateControls function need to be changed as following:
public void CreateControls()
{
try
{
Panel p1= (Panel)Cache["TempPanel"];
panelNameofParticipants.Controls.Add(p1);
//String test1 = test.Value;
List<string> listParticipantName = new List<string>();
if (ViewState["numberofparticipants"] != null)
{
int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
for (int i = 0; i < numberofparticipants; i++)
{
string findcontrol = "txtNameofParticipant" + i;
TextBox txtParticipantName = (TextBox)panelNameofParticipants.FindControl(findcontrol);
listParticipantName.Add(txtParticipantName.Text);
}
}
}
catch (Exception ex)
{
}
}
Hope this helps.
Dynamically created controls would disappear when page posts back, This article here explains why it happens.
So in order to make dynamic controls be recognized by asp.net page, you need to recreate it in preinit event handler, but I think it is difficult as this dynamic controls in your case rely on some other web form element such as a dropdownlist drpNoofparticipants and also ViewState is not available at this stage.
My suggestion is that we do it in a different way, each post back is actually a form post, so instead of finding the dynamic text box, you could directly get the value via Request.Form collection. Here is the code snippet:
var list = Request.Form.AllKeys.Where(x => x.Contains("txtNameofParticipant"));
foreach (var item in list)
{
var value = Request.Form[item];
}
In this way, you could get the value and since you don't need to rely on ASP.NET engine to retrieve the value from dynamically created control, you could postpone the recreating the table in page_load event handler.
Hope it helps.
Put your btnSubmitCountParticipant_Click method data into other function with name of your choice. Call that function in method btnSubmitCountParticipant_Click and in method CreateControls(). Also cut paste the below code in btnSubmitCountParticipant_Click method
int numberofparticipants =
Convert.ToInt32(drpNoofparticipants.SelectedValue);
ViewState["numberofparticipants"] = numberofparticipants;
. This is working in my machine. Hope this helps
Yes,Dynamic controls are lost in postback,So we need save dynamic control values in Viewsate and again generate dynamic control.I added code here, this working fine
//Create Button Dynamic Control
protected void btnDyCreateControl_Click(object sender, EventArgs e)
{
try
{
int numberofparticipants = 5;
ViewState["numberofparticipants"] = numberofparticipants;
int test = (int)ViewState["numberofparticipants"];
int rowcount = 1;
int columnCount = numberofparticipants;
CreateDynamicTable(rowcount, columnCount);
}
catch (Exception ex)
{
}
}
//submit values
protected void btnSave_Click(object sender, EventArgs e)
{
try
{
List<string> listParticipantName = new List<string>();
if (ViewState["numberofparticipants"] != null)
{
int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
foreach (Control c in panelNameofParticipants.Controls)
{
if (c is Table)
{
foreach (TableRow row in c.Controls)
{
int i = 0;
foreach (TableCell cell in row.Controls)
{
if (cell.Controls[0] is TextBox)
{
string findcontrol = "txtNameofParticipant" + i;
TextBox txtParticipantName = (TextBox)cell.Controls[0].FindControl(findcontrol);
listParticipantName.Add(txtParticipantName.Text);
}
i++;
}
}
}
}
}
}
catch (Exception ex)
{
}
}
//Save ViewState
protected override object SaveViewState()
{
object[] newViewState = new object[3];
List<string> txtValues = new List<string>();
foreach (Control c in panelNameofParticipants.Controls)
{
if (c is Table)
{
foreach (TableRow row in c.Controls)
{
foreach (TableCell cell in row.Controls)
{
if (cell.Controls[0] is TextBox)
{
txtValues.Add(((TextBox)cell.Controls[0]).Text);
}
}
}
}
}
newViewState[0] = txtValues.ToArray();
newViewState[1] = base.SaveViewState();
if (ViewState["numberofparticipants"] != null)
newViewState[2] = (int)ViewState["numberofparticipants"];
else
newViewState[2] = 0;
return newViewState;
}
//Load ViewState
protected override void LoadViewState(object savedState)
{
//if we can identify the custom view state as defined in the override for SaveViewState
if (savedState is object[] && ((object[])savedState).Length == 3 && ((object[])savedState)[0] is string[])
{
object[] newViewState = (object[])savedState;
string[] txtValues = (string[])(newViewState[0]);
if (txtValues.Length > 0)
{
//re-load tables
CreateDynamicTable(1, Convert.ToInt32(newViewState[2]));
int i = 0;
foreach (Control c in panelNameofParticipants.Controls)
{
if (c is Table)
{
foreach (TableRow row in c.Controls)
{
foreach (TableCell cell in row.Controls)
{
if (cell.Controls[0] is TextBox && i < txtValues.Length)
{
((TextBox)cell.Controls[0]).Text = txtValues[i++].ToString();
}
}
}
}
}
}
//load the ViewState normally
base.LoadViewState(newViewState[1]);
}
else
{
base.LoadViewState(savedState);
}
}
//Create Dynamic Control
public void CreateDynamicTable(int rowcount, int columnCount)
{
Table tableparticipantName = new Table();
for (int i = 0; i < rowcount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < columnCount; j++)
{
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(j);
cell.ID = "cell" + Convert.ToString(j);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
}
tableparticipantName.Rows.Add(row);
tableparticipantName.EnableViewState = true;
ViewState["tableparticipantName"] = true;
}
panelNameofParticipants.Controls.Add(tableparticipantName);
}
Reference Link,MSDN Link, Hope it helps.
When creating your textboxes make sure you set the ClientIDMode
I recently worked on something similar. I dynamically created checkboxes in GridView rows and it worked for me.
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
txtNameOfParticipant.ClientIDMode = System.Web.UI.ClientIDMode.Static;
Dynamically added controls have to be created again on postback, otherwise the state of the dynamically added controls will get lost (if the controls lost how can one preserve their old state) . Now the question. When is the viewstated loaded? It is loaded in between the two events Page_Init and Load.
Following code samples are a bit modification of your code for your understanding
The aspx markup is same as yours, but with some extra controls
<form id="form1" runat="server">
<asp:DropDownList ID="drpNoofparticipants" runat="server" >
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem>3</asp:ListItem>
<asp:ListItem>4</asp:ListItem>
<asp:ListItem>5</asp:ListItem>
<asp:ListItem>6</asp:ListItem>
<asp:ListItem>7</asp:ListItem>
<asp:ListItem>8</asp:ListItem>
<asp:ListItem>9</asp:ListItem>
<asp:ListItem>10</asp:ListItem>
</asp:DropDownList>
<br /><asp:Button ID="btnCreateTextBoxes" runat="server" OnClick="btnSubmitCountParticipant_Click" Text="Create TextBoxes" />
<div id="divNameofParticipants" runat="server">
<asp:Panel ID="panelNameofParticipants" runat="server">
</asp:Panel>
</div>
<div>
<asp:Button ID="btnSubmitParticipants" runat="server" Text="Submit the Participants" OnClick="BtnSubmitParticipantsClicked" />
</div>
</form>
On click event of the btnCreateTextBoxes button i am creating the controls using the following code
private void CreateTheControlsAgain(int numberofparticipants)
{
try
{
ViewState["numberofparticipants"] = Convert.ToString(numberofparticipants);
Table tableparticipantName = new Table();
int rowcount = 1;
int columnCount = numberofparticipants;
for (int i = 0; i < rowcount; i++)
{
for (int j = 0; j < columnCount; j++)
{
TableRow row = new TableRow();
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(j);
cell.ID = "cell" + Convert.ToString(j);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
tableparticipantName.Rows.Add(row);
}
}
panelNameofParticipants.Controls.Add(tableparticipantName);
}
catch (Exception ex)
{
}
}
As mentioned above to maintain the control state, i included the re-creation of the controls in the page Load event based on the viewstate["numberofparticipants"]
protected void Page_Load(object sender, EventArgs e)
{
if (ViewState["numberofparticipants"] != null)
{
CreateTheControlsAgain(Convert.ToInt32(ViewState["numberofparticipants"]));
CreateControls();
}
}
On click event of btnSubmitParticipants button i wrote the following event and writing the participants names to the console
try
{
//String test1 = test.Value;
List<string> listParticipantName = new List<string>();
if (ViewState["numberofparticipants"] != null)
{
int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
for (int i = 0; i < numberofparticipants; i++)
{
string findcontrol = "txtNameofParticipant" + i;
var txtParticipantName = panelNameofParticipants.FindControl(string.Format("txtNameofParticipant{0}", i)) as TextBox;
listParticipantName.Add(txtParticipantName.Text);
}
}
foreach (var item in listParticipantName)
{
Response.Write(string.Format("{0}<br/>", item));
}
}
catch (Exception ex)
{
}
Hope this helps
I'm not sure if this is desired, but you're adding multiple tables to your panel, but I think you only want/need one table. So this line should be outside the outer for loop like so:
for (int i = 0; i < rowcount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < columnCount; j++)
{
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
cell.ID = "cell" + Convert.ToString(i);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
}
tableparticipantName.Rows.Add(row);
}
panelNameofParticipants.Controls.Add(tableparticipantName);
Additionally, FindControl will only search the direct children of the container, in this case it can only find the table, else it'll return null. so you need to search children of the panel to find the control, e.g. your table:
foreach (TableRow row in tableparticipantName.Rows)
{
TextBox txtParticipantName = (TextBox)row.FindControl(findcontrol);
listParticipantName.Add(txtParticipantName.Text);
}
If that doesn't work then doing it recursively might work better:
ASP.NET Is there a better way to find controls that are within other controls?