Hi I am writing some code for my homework and I am stuck. I created one general click event handler for digit Buttons:
private void btnN_Click(object sender, EventArgs e),
and now I need to make frequency Array which I need to update every time when one of the button is clicked.
Here is freqArray:
private int[] freqArray = new int[10];
How can I do that if there is let say nine buttons?
Thank you
lastNum is number of buttons.
private void btnN_Click(object sender, EventArgs e)
{
for (int i = 0; i < lastNum; i++)
{
freqArray[i]++;
lstFrequencies.Items[i] = i + "\t\t" + freqArray[i];
}
}
Gosh, this is getting out of hand.. but since the original idea of giving hints and helping you along the way has hit the wall - I'll discuss the problem and will leave the decision to you..
The core problem is to identify within the common click event just which of the many buttons has been clicked. The key to this is usually in the event's parameters; in this case the sender is the button that got clicked.
We will look at three different ways to identify the sender:
By comparing it to the Control
By looking at its Name
By using an index stored in its Tag
So the simplest and most direct approach would be something like this:
if (sender == button1) freqArray[0]++;
else if (sender == button2) freqArray[1]++;
..
Note that while the designer names the controls you add with a count from 1 the programmers count from 0! (As do many data structures in C# including your array of counts!)
There are other approaches and since we are doing a little lesson here we go:
Sender is of the most basic type object; you can cast an object to its actual Type like this:
Button b = (Button) sender;
And now you can access all the button's properties, including the Name. So you could write the above code like this:
if (b.Name == "button1") freqArray[0]++;
else if (b.Name == "button2") freqArray[1]++;
..
This has un-coupled the actual button control from the identification and replaced it by a string.
And there is a general purpose property called Tag, which incidentally is also of class object; you can store anything in it. This makes it sometimes quite valuable and often one creates a special structure or even class just to store many things in a Tag..
So if you have created the Buttons to have their Tag contain an index you could use that:
freqArray[ (int) ( (Button) sender).Tag) ]++;
Note the double casts and all those parenthesis! It looks complicated and it is. It is just one line instead of the long vector of if clauses we saw before. But it only works if you have set the Tags before, maybe in the load event:
button1.Tag = 0;
button2.Tag = 1;
button3.Tag = 2;
...
So you have to invest first and can harvest later..
A lot to digest here.. Your pick!
One thing you must understand is you shouldn't ask for approach to a problem. However since your question requires it.
First you should add Tags to the buttons to differentiate them:
button1.Tag = "1";
button2.Tag = "2";
.
.
.
Then in your event handler cast the sender object to your control type:
Button a = (button)sender;
Then check the tag of the button a to add frequencies:
freqArray[Convert.ToString(a.Tag)-48] = freqArray[Convert.ToString(a.Tag)-48] + 1;
Related
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
...
}
What I want to do is creating a menu with several buttons and when the user clicks on one of them, the selected button tag gets stored in a variable and the background color of that button gets highlighted. When the user clicks another button, the previous stored variable gets compared with the new variable and if it is different, it changes the background color of the earlier pressed button back to normal. This is what I have so far:
if (!isSelected)
{
b.BackColor = Color.FromArgb(28, 145, 162);
previousPress = (int)b.Tag;
isSelected = true;
if(previousPress != currentPress)
{
b(previousPress).BackColor = Color.FromArgb(12, 34, 567); // Obviously this wont work, but hopefully it clears up on what I want to reach.
isSelected = false;
currentPress = (int)b.Tag;
}
}
The .Tag idea of B is ranging from 1 till 7, as that is how many buttons are created in the constructor method.
That is what I tried to use, but someone suggested I shouldn't be using tags for this, as it should only cause bugs and errors. He suggested the following:
Button previousButton = b;
if (previousButton != currentButton)
...
I understand the logic behind this, but sadly before he went away before I could ask where and how the currentButton variable is declared/used. Could someone shine some light upon this? Thank you!
Assuming that all the buttons are assigned the same Click event, this code would do exactly what you want:
Color _activeColor = Color.Red;
private void buttons_Click(object sender, EventArgs e)
{
foreach (Button btn in this.Controls.OfType<Button>()
.Where(b => b.BackColor == _activeColor))
{
btn.BackColor = SystemColors.Control;
}
((Button)sender).BackColor = _activeColor;
}
It clears out the button who previously had _activeColor and sets the color to the current one.
This is pretty common approach to what (I believe) you need. Try to understand this code before trying it, it's relying on Linq.
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;
}
I am making a simple class extending CheckedListBox that just adds a small textbox to the right of an item when it is checked. My issue is finding a good way to place the box at the correct location.
I had initially though I could use the Controls.Find() along with the ItemCheckEventArgs index to get the coordinates of the checkbox in question, and move to the right edge of the column from there. However, that did not work, and a brief look through the CheckedListBox class seemed to show that it does not actually contain any CheckBox controls, but merely draws images of them.
I then came up with the following method:
void CreateAmountBox(int index)
{
int itemsPerCol = Height/ItemHeight;
int x = GetColumn(index, itemsPerCol)*ColumnWidth - boxWidth;
int y = (index % itemsPerCol)*ItemHeight - offset;
System.Windows.Forms.TextBox NewAmountTextBox = new System.Windows.Forms.TextBox();
NewAmountTextBox.Location = new System.Drawing.Point(x, y);
NewAmountTextBox.Name = Items[index] + "Amount";
NewAmountTextBox.Size = new System.Drawing.Size(20, boxWidth);
Controls.Add(NewAmountTextBox);
}
where GetColumn(...) returns the column of the given index (from the CheckEventArgs). This works, but it feels like a hack and is not very readable.
Two other ideas I thought of:
1) I could just create all the TextBoxes at the start, and simply hide them until they are needed. Controls like these are all created dynamically throughout the rest of the program however, and I don't want these ones to be the odd exception. It also means that some more functionality needs to be added for cases when an item is added or removed.
2) I could use mouse position, which of course won't work if the input is via keyboard. I don't anticipate it ever being so, but best not to leave that possibility.
With some googling, the only other way I found of possibly doing this was using the ListBoxItem and TranslatePoint method, but I haven't gotten that to work, and I'm unsure as to whether it even can with a CheckedListBox instead of a ListBox.
So, is there a simple way of finding the x and y of the checked item that I don't know of? Or am I limited to simply extracting the x and y declarations above into a method and leaving it there?
You can just use the GetItemRectangle function to accomplish that:
void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) {
Rectangle r = checkedListBox1.GetItemRectangle(e.Index);
TextBox newAmountTextBox = new TextBox();
newAmountTextBox.Location = new Point(r.Left, r.Top);
//...
}
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.