Can we dynamically reference element (Server Side) - c#

Say I have the elements with the ID's of "Input1", "Input2" and "Input3".
Is there a way to loop through them rather then having to write:
Input1.Value = 1;
Input2.Value = 1;
Input3.Value = 1;
in jquery you can just refrence an element like $('#Input'+i) and loop through i, something similar would be very useful in ASP code behind.

Edit: Duh, I searched again for finding all "x" controls on page and came up with the following source code:
foreach(Control c in Page.Controls)
{
if (c is TextBox)
{
// Do whatever you want to do with your textbox.
}
}
Kind of ... based on your example naming scheme you can do something like the following:
private void Button1_Click(object sender, EventArgs MyEventArgs)
{
string controlName = TextBox
for(int i=1;i<4;i++)
{
// Find control on page.
Control myControl1 = FindControl(controlName+i);
if(myControl1!=null)
{
// Get control's parent.
Control myControl2 = myControl1.Parent;
Response.Write("Parent of the text box is : " + myControl2.ID);
}
else
{
Response.Write("Control not found");
}
}
}
This will let you loop through numerically named controls but otherwise it is somewhat clunky.

If you know the parent container you can loop though its .Controls() property. If you start at the Page level and work recursively, you can eventually reach all controls on the page.
See the answer from this question for more details.

I like to keep things strongly typed, so I store them in a list. This makes the code more resilient to refactoring and there's no need to cast. It takes a slight bit more upfront work to put all your controls into the list, but to me it's often worth it.
I'm not sure what type your controls are, so I'm going to pretend they're of type Input.
var InputControls = new List<Input>(){Input1, Input2, Input3};
foreach(var input in InputControls)
{
input.Value = 1;
}

Related

Is there any way that I could "array" a group of pictureBox to use in a for statement? [duplicate]

Has C# indexed control arrays or not? I would like to put a "button array" for example with 5 buttons which use just one event handler which handles the index of all this 5 controls (like VB6 does). Else I have to write for each of these 5 buttons one extra event handler. And if I have 100 buttons, I need 100 event handlers? I mean something like that:
TextBox1[i].Text="Example";
It could make coding definitely easier for me to work with control arrays. Now I have seen, that C# at least has no visible array functionality on user controls and no "index" property on the user controls. So I guess C# has no control arrays, or I must each element call by known name.
Instead of giving 100 TextBoxes in a for loop 100 incrementing values, I have to write:
TextBox1.Text = Value1;
TextBox2.Text = Value2;
...
...
TextBox100.Text = Value100;
A lot of more work + all these 100 event handlers each for one additional TextBox extra.
I know I'm a little late to this party, but this solution will work:
Make a global array:
TextBox[] myTextBox;
Then in your object's constructor, after the call to
InitializeComponent();
initialize your array:
myTextBox = new TextBox[] {TextBox1, TextBox2, ... };
Now you can iterate your array of controls:
for(int i = 0; i < myTextBox.Length; i++)
myTextBox[i].Text = "OMG IT WORKS!!!";
I hope this helps!
Pete
As I mentioned in comment to a solution by HatSoft, C# Winforms does not allow you to create control arrays like old VB6 allowed us. The nearest I think we can get to is what HatSoft and Bert Evans in their posts have shown.
One thing that I hope would satisfy your requirement is the event handler, you get a common event handler and in the event handler when you typecast the "sender" you get the control directly just like you would in VB6
C#
TextBox textBox = sender as TextBox;
VB6
TextBox textBox = TextBox1[i];
So the only trouble you might have is wiring those 100 TextBoxes to a single event handler, if you are not creating the controls dynamically through code rather creating it manually at design time then all one can suggest is group them in a container like say Panel. Then on Form Load wire them all up to a single event handler like this:
foreach (Control control in myTextBoxPanel.Controls)
{
if(control is TextBox)
control.TextChanged += new EventHandler(control_TextChanged);
}
Just create one handler and point all the buttons to it.
var ButtonHandler = (sender, args) => {
var clicked = (Button)sender;
if (clicked.Text == "whatever")
//do stuff
else
//do other stuff
};
button1.Click += ButtonHandler;
button2.Click += ButtonHandler;
Alternatively, if you are creating controls in code, you could use one of the techniques specified in this answer.
Instead of giving 100 TextBoxes in a for loop 100 incrementing values, I have to write:
for(int i = 0; i <100; i++)
{
TextBox t = new TextBox(){ Id = "txt_" + i, Value = "txt_" + i};
t.TextChanged += new System.EventHandler(this.textBox_Textchanged);
Page.Controls.Add(t);
}
//and for event on TextChanged
private void textBox_Textchanged(object sender, EventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
////
}
}
Another thing to note: if you really need to edit 100 strings on one form, you should probably think about whether 100 text boxes is really the best way to do it. Perhaps a ListView, DataGridView, or PropertyGrid would be better suited.
This applies almost any time you think you need a huge array of controls.
If you are working with Web Forms and not MVC, you can acces a collection of controls on the page as shown in Using the Controls Collection in an ASP.NET Web Page. Essentially the controls collection is a tree with the page hosting the first level of child controls and some items having children of their own. See How to: Locate the Web Forms Controls on a Page by Walking the Controls Collection for an example of how to follow the tree.
Also, see How to: Add Controls to an ASP.NET Web Page Programmatically.
You can use the same event handler for multiple items as long as the signature required is the same.
For Windows Forms this is nearly identical since they're based on similar architectural models, but you'll want Control.Controls Property and How to: Add Controls to Windows Forms.
Keeping it simple:
TextBox[] keybox = new TextBox[16]; //create an array
for (int i=0; i<16; i++)
{
keybox[i] = new TextBox(); //initialize (create storage for elements)
keybox[i].Tag = i; //Tag prop = index (not available at design time)
keybox[i].KeyDown += keybox_down; //define event handler for array
}
private void keybox_down(object sender, KeyEventArgs e)
{
int index = (int)((TextBox)sender).Tag //get index of element that fired event
...
}

C# - Add all Input Controls to List<Controls> in TabIndex order

I have a windows form that just exists to take input from user, for all intents and purposes it is just a label and a corresponding input box (textbox, checkbox, masket textbox etc).
I have programatically placed all the input fields in a TabIndex order that is optimal for cycling through them in a way that fits with their locations (tab down each column of inputs, then to the top of the next column).
The person that I am building this project for has stipulated that only like each textbox to come available one at a time, as the previous one has been filled out. This is a crude way of doing it with no validation, but essentially something like this...
if (String.IsNullOrEmpty(textbox1.Text))
{
textbox2.Enabled = true
}
So this is fine to do with two textboxes in this example, but the form has 28 different inputs on it, so an absurd series of if statements will only be a last resort.
My thoughts has been to put all the inputs in a List, ideally in the same order as is their TabIndexes. I tried to do this using a foreach loop...
List<Control> inputsList = new List<Control>();
public void initialiseControls()
{
//control position to insert control into list at specified index
int cntrlpos = 0;
//for every control in form
foreach (Control cntrl in this.Controls)
{
//not counting labels (not input fields)
if (!(cntrl is Label))
{
//set list position to equal control's TabIndex
cntrlpos = cntrl.TabIndex;
//insert the control into the list at the position reflecting TabIndex
inputsList.Insert(cntrlpos, cntrl); //<---- Error Occurs
//ASK TEXTBOX TO OUTPUT LIST POSITION AS TEST
//foreach (var txtbx in this.Controls.OfType<TextBox>())
//{
// txtbx.Text = Convert.ToString(cntrlpos);
//}
}
}
As soon as the function is called, an exception is thrown stating that "Index must be within the bounds of the list".
When I put a breakpoint into the code, it showed cntrlpos to equal 29, which is more than the 28 total input controls there are on the form.
I don't know where to go from here, if anyone can offer some advice on the code above to place the Controls into the list in the correct order (or point me in the direction of another method to do something like this), then I would really appreciate it.
Thanks,
Mark
To make your list, try this:
List<Control> inputList =
(from Control c in getAllControls(this)
where c.TabStop
orderby c.TabIndex
select c).ToList();
Define the method getAllControls elsewhere in your form class:
IEnumerable<Control> getAllControls(Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control descendant in getAllControls(control))
yield return descendant;
}
}
(Taken and modified slightly from Recursive control search with Linq)
This will make it so that you get even nested controls (such as those in panels or groupboxes).
You can't just use the TabIndex as an index into your list, because even stuff like labels have tab indices, which will mess up your indices.
I think you've over complicated it...
Just use Control.GetNextControl:
Retrieves the next control forward or back in the tab order of child
controls.
For example, with just TextBoxes:
private void textBoxes_TextChanged(object sender, EventArgs e)
{
Control ctl = (Control)sender;
if (!String.IsNullOrEmpty(ctl.Text))
{
Control next = this.GetNextControl(ctl, true);
if (next != null)
{
next.Enabled = true;
}
}
}
Obviously you might need a slightly more complicated check for some other types of controls in a different handler, but you'd still just grab the next control to enable using GetNextControl().

Get values from dynamic controls with c#?

I created a few radiobuttonlist controls on my project, they're created every time the page is loaded, i want to get the value of the radiobutton that the user has selected, but since my radiobuttons were created dynamically, i don't know how to acces to their values nor how to create their event handlers. Is there a way to assign a name or id to the control when i create it?
i hope you can help me.
I create a seires of radiobuttlist on the page_load event, with the text and their values been pulled out of a database. now, the user has to choose one of the options from that radiobuttlist and i want to get the value of the radiobutton the user checked. how do i do that if i don't know the name nor the id of the radiobuttlist since they're created dynamically.
this is what i've got:
for (int i = 3; i < numfields; i++) {
if (dr[i].ToString() != "" && dr[i] != null){
r.Items.Add(new ListItem(dr[i].ToString(), dr[i].ToString()));
//r.SelectedIndexChanged += new EventHandler(rowSelectedIndex);
}
}
so basically i use my datareader to loop through the data in the database, if the value from the field isn't empty or null, then i add an item to the radiobuttlist called "r"
i tried to create an eventhandler for that too, but since i have never worked with them i really don't know what to do. :(
I'm so sorry if i seem way too pathetic.
Taking a quick look at your code:
for (int i = 3; i < numfields; i++) {
if (dr[i].ToString() != "" && dr[i] != null){
r.Items.Add(new ListItem(dr[i].ToString(), dr[i].ToString()));
//r.SelectedIndexChanged += new EventHandler(rowSelectedIndex);
}
}
The most obvious thing that jumps out is your if statement. You should first check for null:
if (dr[i] != null && dr[i].ToString() != ""){
As if dr[i] is null, you'll get an exception (as you'll be trying to call the ToString() method on a null object.
If the contents of dr are always going to be strings, you might consider writing:
if(!String.IsNullOrEmpty(dr[i]){
I also note you start your indexing at 3 - is this because you want to skip the first 3 fields?
Wherever you create your variable, 'r', you can set the name and ID properties. You can use the ID property to look for the control on PostBack. So if you created your radiolist like so:
RadioButtonList r = new RadioButtonList();
r.Id = "MyRadioButtonList";
r.SelectedIndexChanged += MyRadioButton_SelectedIndexChanged;
Which would point at the following event handler:
private void MyRadioButton_SelectedIndexChanged(Object sender, EventArgs e) {
... Do Stuff ...
}
There are several ways of finding your control when you post back; you can look in the Request.Forms collection for a control matching the name of the control you submitted, or, more appropriately, you can use the FindControl method with the ID you gave the control. See C#, FindControl for a post with a method (by Jeff Atwood!) that will search the entire hierarchy of controls for your control.
When you add a dynamic control is important, too. If you add it too late in the page lifecycle then it will not be available on PostBack. See http://support.microsoft.com/kb/317515 for more details on just when to add a control. There are plenty of resources for Dynamic ASP.Net controls around too.
You could put your RadioButton into a list as you create them. This is also when you want to add your handlers.
RadioButton rb;
for (int i = 1; i < 5; i++)
{
rb = new RadioButton();
rb.AutoSize = true;
rb.Location = new System.Drawing.Point(25, (i*25) + 25);
rb.Name = "radioButton" + i.ToString();
rb.Text = "radioButton" + i.ToString();
//Add some event handler?
this.Controls.Add(rb);
lstRadioButton.Add(rb);
}
Whenever you want to know which one is selected you can do a foreach loop of your list and look if your RadioButton is checked.
foreach (RadioButton rButton in lstRadioButton)
{
if (rButton.Checked == true)
{
//Do something
}
}
You are maybe searching for TagName property if the programmatic name isn't enough for you.
The problem is that you are creating the controls in page_load. In order for their values to be posted back into the controls correctly, you must move this creation into the page_init method and recreate them every time.
Then, in page_load, you can access the values in the controls correctly. If you give them IDs using a consistent naming convention, you will be able to find them using the FindControl method or, in page_init, you can store them in a collection at the page or user control level.

C# dynamically created control issue

i'm having issues retreiving the values out of a dynamically created dropdownlist. all controls are created in the Page_Init section. the listitems are added at that time as well from an array of listitems. (the controls are named the same so should be accessable to the viewstate for appropriate setting.)
here is the function that attempts to retrieve the values:
protected void Eng98AssignmentComplete_Click(object sender, EventArgs e)
{
String myID = "0";
Page page = Page;
Control postbackControlInstance = null;
// handle the Button control postbacks
for (int i = 0; i < page.Request.Form.Keys.Count; i++)
{
postbackControlInstance = page.FindControl(page.Request.Form.Keys[i]);
//Response.Write(page.Request.Form.Keys[i].ToString());
if (postbackControlInstance is System.Web.UI.WebControls.Button)
{
myID = Convert.ToString(
postbackControlInstance.ID.Replace("button_", ""));
}
}
String txtholder = "ctl00$ContentPlaceHolder$Eng098Instructors_" + myID;
Response.Write("MYID: " + myID + "<br/>");
DropDownList ddInstructorCheck = (DropDownList)Page.FindControl(txtholder);
Response.Write("Instructor Selected: "
+ ddInstructorCheck.SelectedValue + "<br/>");
}
here is the output I get, no matter which instructor was selected.....
MYID: 1_1
Instructor Selected: 0
ctl00$ContentPlaceHolder$Eng098Instructors_1_1
the name of the control is correct (verified via view source)....
ideas?
You're going to a lot of work to build this fancy string:
ctl00$ContentPlaceHolder$Eng098Instructors_1_1
That is the client ID of your control, not the server id. This code is running on the server side, and so you need the server id. To get that control using the server id, you need to do this:
ContentPlaceHolder.FindControl("Eng08Instructors_1_1");
Notice I didn't look in the page, because your content place holder created a new naming container.
Also, the way your loop is set up the myID variable will always end up holding the last button in the Keys collection. Why even bother with the loop?
Based on your comments, a better way to find the id of the dropdownlist is like this:
string id = ((Control)sender).ID.Replace("button_", "Eng098Instructors_");
why not just save the control in an instance in your class so that you don't have to use FindControl?
Do you also re-create the controls during the postback? Dynamically generated/added controls must be re-created with every request, they are not automatically re-created.
Why don't you cast the sender? This should be the button that caused the postback:
string myId = "0";
Button btn = sender as Button;
if (btn != null)
myId = btn.ID
...
You need to perform something like this because the UniqueID property is the key in Request.Form.
List<Button> buttons = new List<Button>();
List<DropDownList> dropdowns = new List<DropDownList>();
foreach (Control c in Controls)
{
Button b = (c as Button);
if (b != null)
{
buttons.Add(b);
}
DropDownList d = (c as DropDownList);
if (d != null)
{
dropdowns.Add(d);
}
}
foreach (String key in Request.Form.Keys)
{
foreach (Button b in buttons)
{
if (b.UniqueID == key)
{
String id = b.ID.Replace("button_", "");
String unique_id = "ctl00$ContentPlaceHolder$Eng098Instructors_" + id;
Response.Write("MYID: " + id + "<br/>");
foreach (DropDownList d in dropdowns)
{
if (d.UniqueID == unique_id)
{
Response.Write("Instructor Selected: " + d.SelectedValue + "<br/>");
break;
}
}
}
}
}
I'm not sure why you are generating the control in code (you can still add items dynamically if you do), but the code that generates the controls would probably be a huge help here. I'm guessing you are not setting the list item value, and instead just setting the list item text. Try seeing what you get from the SelectedText field and post your control creation function.
EDIT:
In response to your comment on #Martin's post, you said "yes I recreate the controls in the Page_Init function each time the page is created (initial or postback)". Are you also setting the selected value when you create them?
You can also use controls on the page even if your data comes from a database, the controls themselves don't have to be dynamically generated.
How about this?
((Button)sender).Parent.FindControl(myid)
Edit:I misunderstood your question. But i think you should follow page lifecycle. it is common issue for dynamically created controls.
I did some research and here is some info about Dynamically Created Controls may help you...
I had 2 catches.... here's what they were.
1. I didn't clear the table I was adding to before re-creating the controls.
apparently my attention to detail was off yesterday, i'm pretty sure the ctlXX frontrunner of the control was some different number upon postback due to how I was recreating the controls.
2. I was assigning the same list to all the dropdownlist controls.
once I called the lookup upon each creation a dropdownlist control, all works well.
anyway for what it's worth....

How to Dynamically get All controls (and it's IDs) in an aspx Page?

HEllo, I need to dynamically activate fields in a page according to the service that is going to be executed...
Let me explain:
There's a page with all the possible fields and a ListBox with all the selected services to be executed, then when the user selects which service to execute (change a car plate, for example), then I need to activate only the field(s) that the service require... (The realationship between Services and Fields are stored in a database).
public void CheckAll(int pService_Id, Control pPage)
{
foreach (Control control in pPage.Controls)
{
busExecutaServico vExecuta = new busExecutaServico();
if (vExecuta.EnableField(control.ID.ToString(), Convert.ToInt32(listBoxServices.SelectedValue)))
{
switch (control.GetType().ToString())
{
case "TextBox":
TextBox controleText = (TextBox)Page.FindControl(control.ID.ToString());
controleText.Enabled = true;
break;
Note that busExecutaServico is the class which contains the method (EnableField) for checking if the selected item matches any field on the database..
I can't seem to get the control.ID.ToString() to work properly (the ID always comes as NULL)
If anyone can help me solve this, or if there's another way (even if it's completely different from what i'm trying), it would be of great help. thanks
I like to use a recursive function for locating controls by either type or ID.
public Control FindControlRecursive(Control rootControl, string controlId)
{
if (rootControl.ID == controlId)
return rootControl;
foreach (Control control in rootControl.Controls)
{
Control foundControl = FindControlRecursive(control, controlId);
if (foundControl != null)
{
return foundControl;
}
}
return null;
}
public Control FindControlRecursive(Control rootControl, Type type)
{
if (rootControl.GetType().Equals(type))
return rootControl;
foreach (Control control in rootControl.Controls)
{
Control foundControl = FindControlRecursive(control, type);
if (foundControl != null)
{
return foundControl;
}
}
return null;
}
You can adapt these to first return a collection of controls, then process them later. Might be easier to keep track of what's happening.
I learned this technique here: http://www.west-wind.com/Weblog/posts/5127.aspx
Be aware that FindControl only searches the current naming container so Page.FindControl will only find controls that are added directly to Page. For example, if you had a repeater control that had the controls you were looking for and it was added to Page, you could find your repeater control via Page.FindControl but it wouldn't find child controls within your repeater, you'd have to recursively perform the FindControl on all container controls in the page.
This might seem a bit strange but it allows you to have controls with the same ID existing on the same page. For example, if you had 10 instances of a user control with textboxes within them called "MyName", you'd really want them to not being over-writing each other's 'MyName' fields!
Your code will come across a null for an ID unless every control has been given an ID.
Also why use:-
TextBox controleText = (TextBox)Page.FindControl(control.ID.ToString());
at all instead of:-
TextBox controleText = (TextBox)control;
and indeed since you only want to change the Enabled property consider:-
((WebControl)control).Enabled = False;
That I suspect will eliminate many case statements.
In your code you don't need to search any control - you already have it in 'control' variable. You even don't need to cast it to TextBox, just to a WebControl, just do this:
...
if (vExecuta.EnableField(control.ID.ToString(), Convert.ToInt32(listBoxServices.SelectedValue)))
((WebControl)control).Enabled = true;
P.S. control.ID is already string, so you should remove any ID.ToString() also.

Categories

Resources