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.
Related
Building a program in which when a button is pushed, it saves that pushed event as a "1" in an array. I want to have 3 buttons, so the array would have 3 fields full of 0s or 1s (Pushed or not pushed) during a dictated length of time. Not too familiar with C#, so I have no idea where to start. Any ideas?
First, you should get familiar with the C# language. A good way to start: https://www.tutorialspoint.com/csharp/
I don't know what your final aim is, but if you really want to go with arrays then try something like this:
// declare it as class member
int[] buttonStates = new int[3] {0, 0, 0}; // immediate initialization
There are several ways to handle button presses. It's depending on the platform you're developing on: WinForms, ASP.NET, WPF, etc.?
But in general:
your button event handlers should contain the code that places the 1s in the array.
// in the first button handler:
buttonStates[0] = 1;
// in the second button handler:
buttonStates[1] = 1;
// in the third button handler:
buttonStates[2] = 1;
Adding to the provided answers and as your title suggests to get Button ID. To identify each button uniquely, you can use 'Name'(unique name for the element to be identified) or 'Tag' (can be used to store custom information about the element) properties of a button.
You can set the button ID in the tag property of button:
Button btn = new Button();
btn.Tag = 1;
Then in your event handler
private void Btn_Click(Object sender, RoutedEventArgs e)
{
Button b = (Button)sender;
int btnTag = (int)b.Tag; //check the Id
}
and can refer to the button using this 'ID'
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);
}
}
Let's say that my program have max 8 buttons and I should be able to switch them on/off (visible/hidden) according to my settings in configuration file.
My chosen buttons should be then visible in two rows (if more than 4) or one row (cnt<=4) on the form leaving no gap between them.(I mean when 1,2,3,4 are active they would be in the first row same 1,2,5,8 is active so the 5 will take place of the button 3 and 8 will take the 4th place)
<add key="butactive" value="1;3;4;8"/>
<add key="but1" value="START"/>
<add key="but2" value="END"/>
<add key="but3" value="PAUSE"/>
<add key="but4" value="RESET"/>
...
The program should be able to determine from "butactive" key which of those buttons will be visible and then change their Text property and add specific action which is also taken from config file.
private void Form2_Load(object sender, EventArgs e)
{
radButton1.Text = ConfigurationManager.AppSettings["but1"];
radButton2.Text = ConfigurationManager.AppSettings["but2"];
radButton3.Text = ConfigurationManager.AppSettings["but3"];
radButton4.Text = ConfigurationManager.AppSettings["but4"];
...
radButton1.Click += getAction(ConfigurationManager.AppSettings["but1a"]);
radButton2.Click += getAction(ConfigurationManager.AppSettings["but2a"]);
...
}
Any idea how to select needed buttons when the form loads and place them in desired positions?
You can use a FlowLayoutPanel as a container for your buttons. If you size it correctly everything you need to do is set the visibility of the buttons and they will arrange themselves as you need:
radButton1.Visible = (bool)ConfigurationManager.AppSettings["butactive1"];
radButton2.Visible = (boll)ConfigurationManager.AppSettings["butactive2"];
...
As for the action, you can use the same event handler for all the buttons and execute whatever you need based on the button that was pressed and what action you have defined:
radButton1.Click += buttonClickHandler;
radButton2.Click += buttonClickHandler;
private void buttonClickHandler(object sender, EventArgs e)
{
var button = sender as Button;
var action = getAction(ConfigurationManager.AppSettings[button.Text + "a"];
//Execute whatever you want here based on action
}
That piece of code you have there is not good, you are creating dependencies on config names with hard codded strings, that is not a good practice at all, if you can access Properties.Default I suggest you to go that way.
For the buttons position you can mess around with positions, for instance you can switch button 3 and 5 positions like this in windows forms:
radButton5.Position = radButton3.Position
But that is not a good approach, what I advice you is to cut that dependency in button numbers out, you can for instance create all buttons hidden and always show the first buttons, the method "getAction" will be a great help in that approach and its not to hard to implement the behavior.
A Scratch (pseudo code) of what you should do would be something like this:
// Get the active buttons from configuration (eg var will be a list of the active buttons names)
var activeButtons = getActiveButtons();
var textBoxNumber = 0
foreach(var button in activeButtons)
{
textBoxNumber++;
TextBox tbx = this.Controls.Find("radButton" + textBoxNumber.ToString(), true).FirstOrDefault() as TextBox;
if(tbx != null)
tbx.Text = ConfigurationManager.AppSettings[button];
}
I'm a newbie in c# and probably going to ask a very easy question, but I've not been able to find anything on the web to help.
I have a tabControl with a TabPage which is containing a TextBox object; this object, when the event "Text changed" is invoked, will perform the change of the parent tabPage's name.
The textbox where I typed "text changed by me" has a method which is managing changing the name of the tabPage:
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (this.textBox1.Text != "")
this.tabControl2.SelectedTab.Text = this.textBox1.Text;
else
this.tabControl2.SelectedTab.Text = "(no name)";
}
Into the current page menu is contained a control to add a new page, which runs this method when the user click on it:
private void addNewPageToolStripMenuItem_Click(object sender, EventArgs e)
{
int numPagine;
string strPagine;
numPagine = this.tabControl2.TabCount;
strPagine = numPagine.ToString();
this.tabControl2.TabPages.Add("new page" + strPagine);
}
...and here is the output, which is expected since I'm just asking to add a new empty tabPage:
So, my question is: how can I make possible that when the user is clicking on "Add new page", rather than creating an empty new tabPage the program is rather creating a page like the first one (i.e. containing a textbox into the same position which has a method to change the text of the parent tabPage that I have just created?
Here is an example.
//..
// create the new page
TabPage tpNew = new TabPage("new page..");
// add it to the tab
this.tabControl2.TabPages.Add(tpNew);
// create one labe with text and location like label1
Label lbl = new Label();
lbl.Text = label1.Text;
lbl.Location = label1.Location;
// create a new textbox..
TextBox tbx = new TextBox();
tbx.Location = textBox1.Location;
tpNew.Controls.Add(lbl);
tpNew.Controls.Add(tbx);
// add code to the new textbox via lambda code:
tbx.TextChanged += ( (sender2, evArgs) =>
{
if (tbx.Text != "")
this.tabControl2.SelectedTab.Text = tbx.Text;
else
this.tabControl2.SelectedTab.Text = "(no name)";
} );
For more complicated layout you may want to consider creating a user control..
You also may want to create the first page with this code; the, of course with real values for text and positions!
For creating a UserControl you go to the project tag and right click Add-UserControl-UserControl and name it, maybe myTagPageUC. Then you can do layout on it like on a form. A rather good example is right here on MSDN
The problem is that is has no connection to the form, meaning you'll have to code all sorts of references to make it work..
I'm not really sure if you may not be better off writing a complete clonePage method instead. It could work like the code above, but would loop over the Controls of the template page and check on the various types to add the right controls..
It really depends on what is more complicated: the Layout or the ties between the pages and the form and its other controls..
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..