Asp.net FindControl + "Multiple controls with the same ID" - c#

I'm getting a really weird behavior with ASP.Net.
When I run the following code, the exception "Multiple controls with the same ID" is thrown.
The exception is not thrown when adding the control but when using FindControl.
What's really weird is that if I put a breakpoint just before the call and run a FindControl call in the immediate windows where the exception is thrown (so far so consistent) but then when I resume the debugger, everything works fine (!!!). The machine runs the same exact code but, it doesn't throw the exception again.
One last thing about this crazy thing, earlier today the very same code was inside Page_Load and everything was working fine but I reorganized the code and moved it to a separate method (which is called by Page_Load).
I'm getting pretty confident this is a ASP.Net bug...
dlAdvanced.DataSource = dsAdvanced;
dlAdvanced.DataBind();
// Load Advanced Values Controls
#region ADV controls
foreach (DataListItem dli in dlAdvanced.Items)
{
DataRow row = dsAdvanced.Tables[0].Rows[dli.ItemIndex];
switch ((string)row["Type"])
{
default:
TextBox tb = new TextBox();
tb.ID = "Input";
dli.FindControl("InputPlace").Controls.Add(tb);
break;
case "System.Int32":
case "System.Decimal":
TextBox tbn = new TextBox();
tbn.ID = "Input";
Image img = new Image();
img.SkinID = "NumberRequired";
img.ApplyStyleSheetSkin(this);
dli.FindControl("InputPlace").Controls.Add(tbn);
dli.FindControl("InputPlace").Controls.Add(img); // Exception happens here
break;
case "System.DateTime":
golf.golfControls.CalendarBox cal = new golf.golfControls.CalendarBox();
cal.ID = "Input";
cal.SkinID = "Calendar";
cal.ApplyStyleSheetSkin(this);
dli.FindControl("InputPlace").Controls.Add(cal);
break;
case "System.Boolean":
RadioButton rb1 = new RadioButton();
rb1.Text = "True";
rb1.ID = "Input";
rb1.GroupName = "grp" + dli.ItemIndex.ToString();
RadioButton rb2 = new RadioButton();
rb2.Text = "False";
rb2.ID = "Input2";
rb2.GroupName = "grp" + dli.ItemIndex.ToString();
dli.FindControl("InputPlace").Controls.Add(rb1);
dli.FindControl("InputPlace").Controls.Add(rb2);
break;
}
}
#endregion
EDIT :
I just though of something and it worked :
DataRow row = dsAdvanced.Tables[0].Rows[dli.ItemIndex];
var inputPlace = dli.FindControl("InputPlace");
switch ((string)row["Type"])
{
default:
TextBox tb = new TextBox();
tb.ID = "Input";
inputPlace.Controls.Add(tb);
break;
case "System.Int32":
case "System.Decimal":
TextBox tbn = new TextBox();
tbn.ID = "Input";
Image img = new Image();
img.SkinID = "NumberRequired";
img.ApplyStyleSheetSkin(this);
inputPlace.Controls.Add(tbn);
inputPlace.Controls.Add(img);
break;
case "System.DateTime":
golf.golfControls.CalendarBox cal = new golf.golfControls.CalendarBox();
cal.ID = "Input";
cal.SkinID = "Calendar";
cal.ApplyStyleSheetSkin(this);
inputPlace.Controls.Add(cal);
break;
case "System.Boolean":
RadioButton rb1 = new RadioButton();
rb1.Text = "True";
rb1.ID = "Input";
rb1.GroupName = "grp" + dli.ItemIndex.ToString();
RadioButton rb2 = new RadioButton();
rb2.Text = "False";
rb2.ID = "Input2";
rb2.GroupName = "grp" + dli.ItemIndex.ToString();
inputPlace.Controls.Add(rb1);
inputPlace.Controls.Add(rb2);
break;
}
So for the time being, my code works fine, but this issue isn't resolved so if someone knows anything about this bug, please enlighten me.

it is not a bug, all your controls has the same ID tb.ID = "Input"; cal.ID = "Input";, etc
try to add a unique string after each ID like
tb.ID = "input" + dli.ItemIndex.ToString();

The problem actually came for an event that tried to add those controls again elsewhere in the code.
I just answer my own question so it's a closed matter, no rage downvote for this responsible behavior please.

Related

Dynamically Create Controls Based Off Data Returned From Query

I am querying access table and with my data that is returned I need to be able to create controls. Now the problem with my code is (I obviously need to learn about loops better than I do) the code executes exactly as it should it does the 1st foreach loop then moves to the 2nd foreach loop. So I have all the labels - then I have all the text boxes. I need it to be a 1 to 1 relationship. So Label Text box. This is my current code that is not producing the desired outcome. Can someone assist me in tweaking this to produce the desired outcome of a 1 to 1 relationship of label to textbox
System.Collections.Hashtable lookup = new System.Collections.Hashtable();
OleDbConnection olecon = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + oName);
olecon.Open();
OleDbCommand command = new OleDbCommand("Query Data Goes Here", olecon);
OleDbCommand command1 = new OleDbCommand("Query Data Goes Here", olecon);
dr = command.ExecuteReader();
while (dr.Read())
{
labelNames.Add(dr[0].ToString());
}
dr.Close();
dr = command1.ExecuteReader();
while (dr.Read())
{
textboxNames.Add(dr[0].ToString());
}
dr.Close();
foreach (string label in labelNames)
{
Label lbl = new Label();
lbl.Name = "lbl_" + index;
lbl.Text = label;
lbl.AutoSize = true;
Form1.Controls.Add(lbl);
index++;
}
foreach (string textbox in textboxNames)
{
TextBox tbx = new TextBox();
tbx.Name = "txt_" + counter;
tbx.AutoSize = true;
Form1.Controls.Add(tbx);
counter++;
}
I don't see any use of textboxNames collection. What you need is, group the two foreach and create label and textbox together and add them to your Form like below
foreach (string label in labelNames)
{
Label lbl = new Label();
lbl.Name = "lbl_" + index;
lbl.Text = label;
lbl.AutoSize = true;
Form1.Controls.Add(lbl);
TextBox tbx = new TextBox();
tbx.Name = "txt_" + index;
tbx.AutoSize = true;
Form1.Controls.Add(tbx);
index++;
}
Rather than doing them as 2 separate foreach loops you could do it in a single for loop, e.g. (assuming labelNames and textboxNames are List<string>):
for (int i = 0; i < labelNames.Count; i++)
{
Label lbl = new Label();
lbl.Name = "lbl_" + labelNames[i];
lbl.Text = labelNames[i];
lbl.AutoSize = true;
Form1.Controls.Add(lbl);
TextBox tbx = new TextBox();
tbx.Name = "txt_" + textboxNames[i];
tbx.AutoSize = true;
Form1.Controls.Add(tbx);
}
Might also be worth checking there are equal numbers of each first as a sanity check:
if (labelNames.Count != textboxNames.Count)
{
//throw exception etc.
}
Replace your two foreach loops with a single for loop that iterates through both lists at the same time.:
for(int i = 0; i < Math.Min(labelNames.Length, textboxNames.Length); i++)
{
Label lbl = new Label();
lbl.Name = "lbl_" + i;
lbl.Text = textboxNames[i];
lbl.AutoSize = true;
Form1.Controls.Add(lbl);
TextBox tbx = new TextBox();
tbx.Name = "txt_" + i;
tbx.AutoSize = true;
Form1.Controls.Add(tbx);
}
The "Math.Min(labelNames.Length, textboxNames.Length)" comparator will make sure the loop stops after whichever list has the fewest entries. I didn't see either of your labelNames or textboxNames collections defined so I'm not sure whether they're arrays or lists or what, so you may need to change "Length" to "Count".

When i read the xml file it reads duplicates of the node

When i try read my xml file it seems to reads it twice and im unsure on how to fix such an error. I thought i can have some sort of loop but im still lost. Any help would be appreciated ty. - It writes the xml file correctly but the duplicate occurs when it reads it.
String workingDir = Directory.GetCurrentDirectory();
XmlTextReader textReader = new XmlTextReader(workingDir + #"\xmldoc.xml");
Console.WriteLine("BaseURI:" + textReader.BaseURI);
textReader.Read();
while (textReader.Read())
{
if (textReader.Name == "test")
{
textReader.Read();
XmlNodeType nType = textReader.NodeType;
if (nType == XmlNodeType.Text)
{
// label.Text = textReader.Value.ToString();
Label l = new Label();
System.Drawing.Point l1 = new System.Drawing.Point(15, 13 + a);
l.Location = l1;
l.Text = textReader.Value.ToString();
a += 20;
}
What makes you think some entries are read twice? If it is the case, also check if this method is not called twice (shift + F12 in Visual Studio to find usage).
Also, it seems that the piece of code you joined here is not complete (no declaration of variable 'a'). Do you have some code executed under the if (textReader.Name == "test") that would do the same operations?
I have no idea what you are REALLY trying to do, and no visibility of you XML, but here is roughly how I would do it.
Notes:
I'm using an XmlReader <- Better IMHO (base class, so there's less waffle)
I'm using "reader.Value" without the ToString() as it's already a string type.
I changed it to be a switch as I think they are cleaner, but there are A LOT of XmlNodeTypes out there and you don't want to have to if/else to much!!!
Code:
XmlReader reader = XmlReader.Create("books.xml");
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element: // The node is an element.
//DO NOTHING
break;
case XmlNodeType.Text: //Display the text in each element.
//label.Text = reader.Value;
Label l = new Label();
System.Drawing.Point l1 = new System.Drawing.Point(15, 13 + a);
l.Location = l1;
l.Text = reader.Value;
a += 20;
break;
case XmlNodeType.EndElement: //Display the end of the element.
//DO NOTHING
break;
}
}

Add textbox via dynamic imagebutton in a panel

I have a problem I seem to stumble over all the time, I have a Drop Down box and you can select a number which creates x number of textboxes with images buttons its for a survey it the image buttons are used to create "Sub-Answers" so they can have answers to answers so my question is I need to when they hit the image button to create a textbox under the orginal textbox here is the code.
for (Int32 i = 1; i <= NumberOfAnwsers; i++)
{
Literal l1 = new Literal();
l1.Text = "<tr><td>Answer " + i + " text.</td><td>";
TextBox tb = new TextBox();
tb.ID = "TextBoxAnswer" + i;
tb.EnableViewState = false;
tb.Width = 300;
Literal l3 = new Literal();
l3.Text = "</td><td>";
Literal l2 = new Literal();
l2.Text = "</td></tr>";
RadColorPicker CPI = new RadColorPicker();
CPI.PaletteModes = PaletteModes.WebPalette;
CPI.ID = "RadColorPicker" + i;
CPI.ShowIcon = true;
CPI.SelectedColor = System.Drawing.Color.Black;
ImageButton IBVideo = new ImageButton();
IBVideo.ID = "IBVideo" + i;
IBVideo.ImageUrl = "/images/video-icon.jpg";
IBVideo.ToolTip = "Add Video";
IBVideo.Height = 20;
IBVideo.Width = 20;
ImageButton IBAdd = new ImageButton();
IBAdd.ID = "IBAdd" + i;
IBAdd.ImageUrl = "/images/add-icon.png";
IBAdd.ToolTip = "Add Sub-Answers";
//IBAdd.OnClientClick = "showDialog(" + i + ");return false;";
IBAdd.Height = 20;
IBAdd.Width = 20;
//Add Textbox
PanelAnswersToQuestions.Controls.Add(l1);
PanelAnswersToQuestions.Controls.Add(tb);
PanelAnswersToQuestions.Controls.Add(l3);
PanelAnswersToQuestions.Controls.Add(CPI);
PanelAnswersToQuestions.Controls.Add(IBVideo);
PanelAnswersToQuestions.Controls.Add(IBAdd);
PanelAnswersToQuestions.Controls.Add(l2);
}
As you can see I just add controls to the panel, I need to know when that ImageBUtton is hit I can add a Textbox and in this case it could be more then just one textbox to it.
I hope this is clear but for some reason I dont think it is ... sorry.
I have added a radwindow and poping that up sending the Data to the partent via javascript the which created a new problem for me, I can not in javascript seem to find the dynamicly created hiddenfield
function OnClientClose(radWindow) {
var oWnd = $find("<%=RadWindowAddSubAnswer.ClientID%>");
var SubAnswerValues = oWnd.get_contentFrame().contentWindow.document.forms(0).HiddenFieldSubAnswers.value;
alert(SubAnswerValues);
var AnswerID = oWnd.get_contentFrame().contentWindow.document.forms(0).HiddenFieldAnswerID.value;
alert(AnswerID);
var HiddenName = "HiddenFieldSubAnswers" + AnswerID;
alert(HiddenName);
document.getElementById(HiddenName).value = SubAnswerValues;
$get("DivSubAnswers" + AnswerID).innerHTML = SubAnswerValues;
}
The "document.getElementById(HiddenName).value = SubAnswerValues;" seems to never be found, I also tried $get(HiddenName).value = SubAnswerValues; that does not seem to work either both come back as null as for the code behind its:
HiddenField HFSubAnswers = new HiddenField();
HFSubAnswers.ID = "HiddenFieldSubAnswers" + i;
HFSubAnswers.Value = "0";
Im not sure if I got your question right but if you need to dynamically add controls on a Page here is what I can say.
Before adding your control I guess you need to find the control where you need to add it on, Add the control then assign the properties.
PlaceHolder myPlaceHolder = (PlaceHolder)Page.FindControl("PlaceHolder1");
myPlaceHolder.Controls.Add(myButton);
myButton.Text = "Hello World";
For a more detailed expalnation go here http://anyrest.wordpress.com/2010/04/06/dynamically-removing-controls-in-a-parent-page-from-a-child-control/

Controls.remove() method not working in asp.net

I have a web app where the user can create dynamic textboxes at run time. When the user clicks SUBMIT, the form sends data to the database and I want remove the dynamic controls.
The controls are created in the following code:
Table tb = new Table();
tb.ID = "tbl";
for (i = 0; i < myCount; i += 1)
{
TableRow tr = new TableRow();
TextBox txtEmplName = new TextBox();
TextBox txtEmplEmail = new TextBox();
TextBox txtEmplPhone = new TextBox();
TextBox txtEmplPosition = new TextBox();
TextBox txtEmplOfficeID = new TextBox();
txtEmplName.ID = "txtEmplName" + i.ToString();
txtEmplEmail.ID = "txtEmplEmail" + i.ToString();
txtEmplPhone.ID = "txtEmplPhone" + i.ToString();
txtEmplPosition.ID = "txtEmplPosition" + i.ToString();
txtEmplOfficeID.ID = "txtEmplOfficeID" + i.ToString();
tr.Cells.Add(tc);
tb.Rows.Add(tr);
}
Panel1.Controls.Add(tb);
The Remove section of the code is:
Table t = (Table)Page.FindControl("Panel1").FindControl("tbl");
foreach (TableRow tr in t.Rows)
{
for (i = 1; i < myCount; i += 1)
{
string txtEmplName = "txtEmplName" + i;
tr.Controls.Remove(t.FindControl(txtEmplName));
string txtEmplEmail = "txtEmplEmail" + i;
tr.Controls.Remove(t.FindControl(txtEmplEmail));
string txtEmplPhone = "txtEmplPhone" + i;
tr.Controls.Remove(t.FindControl(txtEmplPhone));
string txtEmplPosition = "txtEmplPosition" + i;
tr.Controls.Remove(t.FindControl(txtEmplPosition));
string txtEmplOfficeID = "txtEmplOfficeID" + i;
tr.Controls.Remove(t.FindControl(txtEmplOfficeID));
}
}
However, the textboxes are still visible.
Any ideas?
I would assume that your creating these controls each time the page is loaded or else when your remove code ran on postback it would fail because they would not exist.
So you need to move the create code to NOT happen with every page load by making sure it is wrapped in a if (!IsPostBacK) { ... } statement.
If you do this then you wouldn't need to remove them manually since they are created dynamically and therefore not created by default on each postback.
If you can post where the code that creates the controls is being called from, and the remove as well, I could help you a bit more.
What I ended up doing was instead of deleting the textbox, I deleted the TableRow;

Removing dynamically created controls

I am attempting to remove dynamically created controls in C# 2008 asp.net
The controls are created here:
int i;
for (i = 0; i < myCount; i += 1)
{
TextBox txtAuto = new TextBox();
TextBox txtModel = new TextBox();
TextBox txtMiles = new TextBox();
TextBox txtVINumber = new TextBox();
TextBox txtPlateNumber = new TextBox();
txtAuto.ID = "txtVehAuto" + i.ToString();
txtModel.ID = "txtVehModel" + i.ToString();
txtMiles.ID = "txtVehMilage" + i.ToString();
txtVINumber.ID = "txtVehVINumber" + i.ToString();
txtPlateNumber.ID = "txtVehPlate" + i.ToString();
phAuto.Controls.Add(txtAuto);
phModel.Controls.Add(txtModel);
phMiles.Controls.Add(txtMiles);
phVINumber.Controls.Add(txtVINumber);
phPlateNumber.Controls.Add(txtPlateNumber);
dyntxtAuto[i] = txtAuto;
dyntxtModel[i] = txtModel;
dyntxtMiles[i] = txtMiles;
dyntxtVINumber[i] = txtVINumber;
dyntxtPlateNumber[i] = txtPlateNumber;
LiteralControl literalBreak = new LiteralControl("<br />");
phAuto.Controls.Add(literalBreak);
phModel.Controls.Add(literalBreak);
phMiles.Controls.Add(literalBreak);
phVINumber.Controls.Add(literalBreak);
phPlateNumber.Controls.Add(literalBreak);
}
}
How can I remove the controls?
The user will click "submit" at which point the user entered data will be written to the db. The page will retrun back to itself (with blank fields). I want to remove the dynamic controls after the data has been written to the db. Remove meaning delete, not hide. I tried to set ViewState to false, but it doesn't help.
Can you do this?
phAuto.Controls.Clear();
phModel.Controls.Clear();
phMiles.Controls.Clear();
phVINumber.Controls.Clear();
phPlateNumber.Controls.Clear();
Are there other controls in these controls that I'm not aware of that you do not want cleared?
It depends on what you are trying to do...
If you are simply wanting to remove ALL the controls within your placeholder "phAuto", "phModel", etc... you can use the *.Controls.Clear(); method.
However, if you're looking to remove individual controls... you can use the *.FindControl(string id) method. It will return a generic Control object of with the id you pass to it. You could then pass that object to the *.Controls.Remove(...) method.
If you're wanting to use Page.FindControl(), Page.Controls.Remove(), etc AND you're using master pages... it gets kinda tricky. Read this article to learn about Recursive Control searching.
I'm still new to .NET myself and I'm finding new (and easier) ways of doing things daily. I would not be surprised if there's a better way of doing this.
Good Luck!
private List<Control> ControlList = new List<Control>;
private void CreateControls();
{
int i;
for (i = 0; i < myCount; i += 1)
{
TextBox txtAuto = new TextBox();
TextBox txtModel = new TextBox();
TextBox txtMiles = new TextBox();
TextBox txtVINumber = new TextBox();
TextBox txtPlateNumber = new TextBox();
txtAuto.ID = "txtVehAuto" + i.ToString();
txtModel.ID = "txtVehModel" + i.ToString();
txtMiles.ID = "txtVehMilage" + i.ToString();
txtVINumber.ID = "txtVehVINumber" + i.ToString();
txtPlateNumber.ID = "txtVehPlate" + i.ToString();
ControlList.Add(txtAuto);
ControlList.Add(txtModel);
ControlList.Add(txtMiles);
ControlList.Add(txtVINumber);
ControlList.Add(txtPlateNumber);
phAuto.Controls.Add(txtAuto);
phModel.Controls.Add(txtModel);
phMiles.Controls.Add(txtMiles);
phVINumber.Controls.Add(txtVINumber);
phPlateNumber.Controls.Add(txtPlateNumber);
dyntxtAuto[i] = txtAuto;
dyntxtModel[i] = txtModel;
dyntxtMiles[i] = txtMiles;
dyntxtVINumber[i] = txtVINumber;
dyntxtPlateNumber[i] = txtPlateNumber;
LiteralControl literalBreak = new LiteralControl("<br />");
phAuto.Controls.Add(literalBreak);
phModel.Controls.Add(literalBreak);
phMiles.Controls.Add(literalBreak);
phVINumber.Controls.Add(literalBreak);
phPlateNumber.Controls.Add(literalBreak);
}
}
private void RemoveControls(List<Control> ControlList)
{
foreach (Control item in ControlList)
{
item.Remove();
}
}
I haven't used ASP.NET in a while, and this is untested, but I would assume this (or a variation of it) would work. I can't remember if List<Control> works or not... Might have to make it List<TextBox>.

Categories

Resources