Dynamically created button creation, Loop not creating unique ID's - c#

I am working on creating buttons that are created dynamically based the results of a SQL Query:
private void createPagingButtons(DateTime firstDayofWeek, DateTime lastDayofWeek)
{
int i = 1;
SqlDataReader returnedQuery = getDefaultUser(firstDayofWeek, lastDayofWeek);
while (returnedQuery.Read())
{
string buttonName = returnedQuery["Person"].ToString();
System.Diagnostics.Debug.WriteLine(buttonName);
Button btn = new Button();
btn.ID = i.ToString();
btn.Click += new EventHandler(btn_Click);
btn.Text = buttonName;
pagingPanel.Controls.Add(btn);
i++;
}
}
The way I am trying to assign unique button ID's is by assigning them a number that is incremented each time the while loop iterates:
btn.ID = i.ToString();
But it's not working and I am getting an error:
Multiple controls with the same ID '1' were found. FindControl requires that controls have unique IDs.
WHy is this happening and how can I fix it?

The only way this could occur is if this method were executed more than one time or if these buttons are somehow persisted during the load and then you call it again. However, dynamic controls are for all intents and purposes not persisted and must be recreated on each post back to be useful.
Thus my conclusion, you're calling it more than once.

As other users said, this error will be thrown if the function is called more than once. Your function will re-start at one each time it is called. Which will duplicate the IDs.
Assuming this is actually the desired behavior, that your function can be called multiple times and will create new buttons each time. Making your int i = 1; a class level variable would resolve this.

Replace i++ with ++i.. This should solve your issue..

Related

Creating Dynamic Controller Names in C# with WinForms for TableLayoutPanel

While working on a small app that pulls test cases, runs, and results from an SQL Server Database, I encountered a dilemma in my methodology for attempting to create dynamic controller names in a TableLayoutPanel in WinForms. I am creating the rows dynamically when the user chooses the particular test case, and from there the TableLayoutPanel will open another window with the test steps preloaded and two radio buttons to indicate whether or not the test passed. My issue is that when I select one of the radio buttons on the right of the step, I get the same console read every single time. I need to be able to determine which exact radio button the user has pressed so I can therefore determine what row it's in and subsequently what test either passed or failed. My main code is as follows:
FormManualTest.cs (section when adding to the TableLayoutPanel)
private void addRowToolStripMenuItem_Click(object sender, EventArgs anotherEvent)
{
tableLayoutTest.RowStyles.Clear(); // Clear row styles to ensure a clean start when adding to the TableLayoutPanel
List<RadioButton> listOfRadioControls = new List<RadioButton>(); // Create array of radio buttons
List<UserCustomStep> listOfStepControls = new List<UserCustomStep>(); // Create array of custom controls
for (int i = 0; i < 5; i++)
{
UserCustomStep step = new UserCustomStep(Counter, "Step: " + i + " Push the button to elicit a response."); // Creates new user custom step control instance
RadioButton pass = new RadioButton();
pass.Text = "Pass";
pass.AutoSize = true;
RadioButton fail = new RadioButton();
fail.Text = "Fail";
fail.AutoSize = true;
fail.Margin = new Padding(3,3,20,3); // Needed to see the fail button without having to scroll over
listOfStepControls.Add(step); // Add step to UserCustomStep array
listOfRadioControls.Add(pass); // Add radio buttons to the RadioButton array
listOfRadioControls.Add(fail);
listOfRadioControls[i * 2].CheckedChanged += (s, e) => // Subscribes the pass radio button to listen for when a user has clicked on it
{
Console.WriteLine("Pass " + i + " was clicked");
};
listOfRadioControls[(i * 2) + 1].CheckedChanged += (s, e) => // Subscribes the fail radio button to listen for when a user has clicked on it
{
Console.WriteLine("Fail " + i + " was clicked");
};
tableLayoutTest.Controls.Add(listOfStepControls[i], 0, i); // Adds CustomStep to first column
tableLayoutTest.Controls.Add(listOfRadioControls[i*2], 1, i); // Adds Pass Radio Button to second column
tableLayoutTest.Controls.Add(listOfRadioControls[(i * 2) + 1], 2, i); // Add Fail Raido Button to third column
Counter++; // Increment couter to add subsequent steps underneath the previous ones.
}
}
Screenshots of App with Console Readout:
After Test Case Has Been Clicked and Radio Button Has Been Pressed
(From clicking this I would expect the console to read "Pass 1 was clicked")
Console Read:
Click Fail Button:
(I know from this image below that since the Pass button doesn't remain clicked I'm somehow using the same controller for all 5 of them)
Console Read
So from all of these issues that I've been presented with, I know that I'm somehow using the same controller for all 5 instances regardless of the fact that I'm storing everything in a controller array and grabbing from there. The for loop will have to be converted to a for each loop later, but that still doesn't solve my issue. I believe that if I could say something like:
RadioButton (pass+id) = new RadioButton();
or something similar while looping through to dynamically create the name for the controls, then each one would be a completely separate control and I could go from there. Any help would be greatly appreciated! I come from a heavy web background so my normal skills to remedy this in JS land aren't coming in handy as of right now. Thanks again for the assistance.
The Name property is optional, you don't need to specify it and it doesn't need to be unique. You can use property Tag for your own purpose (you can assign there ID or event instance of some object).
However you can also create your own control/usercontrol which encapsulate the whole row, and you can declare your own properties exactly for your purpose.

Dynamic Text Box Add on button click and data session in asp.net

QUESTION UPDATE
I am using this piece of code for cor adding multiple labels on button click.
But each and every time it gives count value 0 and after execution only same label comes.
int count = (int)ViewState["count"];
for (int i = 0; i <= count; i++)
{
TextBox txtnew = new TextBox();
txtnew.ID = "label_" + (i + 1);
txtnew.Text = "label_" + (i + 1);
ViewState["count"] = i;
pnlControl.Controls.Add(txtnew);
}
ViewState["count"] = count + 1;
What i want now is how to keep that data of each control binded to it in its more convenient way.
Dynamic controls are lost on every PostBack, so you need to keep track of the created ones somewhere and recreate them when the page is reloaded.
See my anwer here: How to dynamically create ASP.net controls within dynamically created ASP.net controls
It is about buttons but the basic principle is the same. Just replace Button with Label and it will work.
That's cause it's a web application and thus the session is not retained cause web applications are stateless in nature. So, every post back you get a new page instance which will have int count is 0. Solution: Use Session to store the data for future re-use like
int count = pnlControl.Controls.OfType<Label>().ToList().Count;
Session["count"] = count;
Retrieve it back on postback request
if(Session["count"] != null)
count = (int)Session["count"];

Session won't store attribute on PostBack when CheckBoxList is triggered

My problem is that my List is not being retained during PostBack.
I am saving it into
Page.Session["mine"]
My design page consists of 2 controls( a Label and a CheckBoxList).
Because the number of items will change, I use a CheckBoxList instead of individual CheckBox controls and load each item dynamically in the page_load();
I am unsure of what it is called, either a member, attribute, or property. But I do have a variable that each method of the class are able to call on that holds the location of all selected items on the CheckBoxList.
Private List<int> locCheck = new List<int>();
When the OnSelectedIndexChanged() is triggered, the locCheck is renewed and stored in Session
locCheck = new List<int>();
int num = 0;
foreach(ListItem li in CheckBoxList.Items)
{
if(li.Selected)
locCheck.Add(num);
num++;
}
if(locCheck.Count == 0) //for testing purpose
locCheck.Add(1); //2nd item
Page.Session["mine"]=locCheck;
I want to keep the selected checks on PostBack, but it never happens. In Visual Studio I have no problem. But when I load to server, I am using mono, I get the following message:
Object reference not set to an instance of an object
I have this in page_load
if(Page.IsPostBack)
{
locCheck = new List<int>();
locCheck = (List<int>) Page.Session["mine"];
Label.Text = String.Format("Is PostBack {0}/{1}", locCheck.Count, CheckBoxList.Items.Count);
}
else
{
Label.Text = String.Format("Is Not PostBack 0/{0}", CheckBoxList.Items.Count);
}
I know the problem is with this line because locCheck.Count never changes in Label.Text
locCheck =(List<int>) Page.Session["mine"];
I know I have to attach
if(Page.Session["mine"] != null)
But I don't believe it is currently necessarry. Due to testing purposes, it should always have locCheck with at least one element.
i couldn't find an answer, so i started from scratch. instead of checklists i use panels of checkboxes that turn invisible when not needed.
I am still uncertain about Sessions but I have found something strange.
For example:
Session["mine"] = locCheck; //where lockCheck has 4 elements
there are times that locCheck is set to null sometime between page_load to a button click.
i have found that when this is so, debug back to a point where locCheck is not null and has correct element values. then perform following:
Session["mine"] = locCheck;
and later on, you perform the following code in click event
locCheck = new List<int>();
locCheck = (List<int>) Session["mine"];
//then make your update:
locCheck[3] = 7;
Session["mine"] = locCheck;
for some reason, if i don't do this both Session["mine"] and locCheck become null and data is lost.
try to use this
locCheck = (List<int>) Page.Session["mine"].ToString();
why to use page.session when you can use only session.

Removing Items From Panel on a Timer

Background
I have a table of items that the user can edit. They can navigate through the fields with the arrow keys, and the information in the current row is saved when they move to a new one. The project requires a notification be displayed to the user when the row was successfully saved, and my boss requires me to do this through Bootstrap alerts.
One alert needs to be created for each successful save, and if the user saves multiple rows in a short time they should stack inside of a panel. Because the number of alerts is unknown, I'm dynamically adding them to the page from the code-behind. Each alert is made up of a panel and a label, which is being added to a larger panel that holds all the alerts. This part I've got figured out -- the alerts show up when they're supposed to, and in the correct numbers.
The problem is that each alert is only supposed to show on the screen for a limited amount of time. This is somewhere between two and five seconds, to be determined by my boss at a later date. My idea was to start a timer for two seconds each time an alert is created, and remove the first alert from the panel when the timer finished. Because no two timers would be created at exactly the same time, this should theoretically remove each alert two seconds after it appears, stopping once the last alert is gone. Unfortunately for me, that isn't how it's working.
Instead, I get an 'index out of bounds' exception, indicating that the alert I'm trying to remove doesn't actually exist. But it does exist -- I can see it on my screen. So I'm not sure what's going wrong.
Code
Creation of Alerts
This code is inside of Page_Load, so that the alerts are still visible on postback.
if (Session["success"] != null)
{
int test = Convert.ToInt32(Session["success"]);
for(int a = 1; a <= test; a++)
{
Panel alert = new Panel();
alert.CssClass = "alert alert-success";
alert.ID = "dynamicAlert" + a;
alert.Attributes["role"] = "alert";
Label innerAlert = new Label();
innerAlert.ID = "dynamicAlertInner" + a;
innerAlert.Text = "<strong>Success!</strong> Your row was saved.";
alert.Controls.Add(innerAlert);
alertsUpdate.ContentTemplateContainer.FindControl("pnlAlerts").Controls.Add(alert);
System.Timers.Timer time = new System.Timers.Timer(2000);
time.Elapsed += removeAlert;
time.Start();
}
}
Deletion of Alerts
The removeAlert method is intended to remove the alert at index 0 from the panel contained within the update panel.
private void removeAlert(Object source, System.Timers.ElapsedEventArgs e)
{
Panel pnl = (Panel)alertsUpdate.ContentTemplateContainer.FindControl("pnlAlerts");
if(pnl != null && pnl.Controls.Count > 0)
{
pnl.Controls.RemoveAt(0);
}
}

How to add class for only the selected link in C# code behind?

I have a class called "is-active" and it has a colored arrow that sticks out from the nav into the main content based on which link the user clicked. The code runs a foreach and pulls all the categories from the database. How do I get the "is-active" class to display only for the current link? I know it works since I put it in the openList control and it displayed on all five categories, I just don't know how to get it to display on only the selected category.
I tried attaching jQuery to do it but adding the linkbutton is done all in the code behind so I am not sure how to attach the two. Is this the only way or is there another way?
Thank you in advance for your help!
Below is my code for the categories and link button:
protected override void CreateChildControls()
{
LiteralControl openingDiv = new LiteralControl("<div id='MainPanel'>");
LiteralControl closingDiv = new LiteralControl("</div>");
this.Controls.Add(openingDiv);
foreach (DataRow dr in ds.Tables[0].Rows)
{
LiteralControl openList = new LiteralControl("<li class='" + dr["CategoryColor"].ToString() + "'>");
LiteralControl closeList = new LiteralControl("</li>");
Label lblNumber = new Label();
LinkButton myLinkButton = new LinkButton();
myLinkButton.Text = "<span class='number'>" + dr["CategoryNumber"] + "</span>"+ dr["CategoryName"].ToString();
myLinkButton.CommandArgument = dr["Category_ID"].ToString();
myLinkButton.Click += myLinkButton_Click;
this.Controls.Add(openList);
this.Controls.Add(myLinkButton);
this.Controls.Add(closeList);
}
this.Controls.Add(closingDiv);
}
void myLinkButton_Click(object sender, EventArgs e)
{
LinkButton btn = (LinkButton)(sender);
Session["CategoryID"] = btn.CommandArgument;
Response.Redirect(Request.RawUrl);
}
Its tricky because your response.redirecting in the button click handler which recreates the pages viewstate.
This means that your page will always appear as fresh and the fact a user has clicked that link has been lost.
as a workaround you could place the link id in a session variable before you response.redirect and then recall it when the page reloads.
then when in your loop, if the session variable matches the current button instance.id you set cssclass equal to is-active.
remember to clear the session variable too after you set the cssclass to is-active to avoid confusion in other pages.
also, you will have to do the id comparison after you add the button to the control tree, because this is where the system automatically generates the id for you. I can give you a full example if you wish.
keep in mind that this is a workaround and a different approach would be best.
by this I mean that if you were to use an ispostback wrapper instead of a redirect, then on postback you could set the id to isactive much easier.
Page lifecycle is an important thing to get your head around in .net if you wish to be more proficient, especially as you mention that your using ajax update panels.
Read this: http://msdn.microsoft.com/en-us/library/ms178472(VS.100).aspx it's a lot of information to take in, so bookmark it for later too as you will use it a lot.

Categories