I'm currently trying to add a specific action to an array of labels but the variables are kept only between the {}.
_arr[i].Click += (a, b) => {
z++;
numarfinal = Convert.ToString(z);
MessageBox.Show(Convert.ToString(numarfinal));
};
Outside {} neither variable z nor numarfinal has changed but the MessageBox.Show reports that their parameters have changed.
Here is the whole function:
Label[] _arr = new Label[20];
Label[] _dinamic = new Label[20];
private static Random _r = new Random();
string numarfinal ;
private void button1_Click(object sender, EventArgs e)
{
int z=0;
int limita = Convert.ToInt16(textBox1.Text);
limita = int.Parse(textBox1.Text);
if (limita > 20)
textBox1.Text = "Do sth";
int randomnumber = _r.Next(20);
for(int i=0;i<limita;i++)
{
do
{
randomnumber = _r.Next(20);
} while (randomnumber==0);
_arr[i] = new Label();
_arr[i].Click += (a, b) =>
{
z++;
numarfinal= Convert.ToString(z);
MessageBox.Show(Convert.ToString(numarfinal));
};
_arr[i ].Text = Convert.ToString(randomnumber);
_arr[i ].Size = new Size(50,50);
_arr[i ].Location = new Point(55*i,60);
testlabel.Text = Convert.ToString(numarfinal); // the label value remain nothing (numarfinal's initial value)
this.Controls.Add(_arr[i]);
}
The problem is that you set testlabel.Text once, while the value of numarfinal is still null. When one of the labels is clicked and its event handler is executed, the value of numarfinal is changed, but the value of testlabel.Text isn't.
One way to fix that is to simply set testlabel.Text in the event handler lambda:
_arr[i].Click += (a, b) =>
{
z++;
numarfinal = Convert.ToString(z);
testlabel.Text = numarfinal;
MessageBox.Show(Convert.ToString(numarfinal));
};
The z value is being changed in the click events of the labels. If you want to see the change in your testlabel you should change its text "in the click events". If you want to change numarfinal every time you add a label you should put your code out of click events and if you want numarfinal change every time you click on a label your code should be were it already is.
Clicking button1 will never cause the line numarfinal = Convert.ToString(z); to be reached because your are only attaching the delegate there. When you assign the delegate with (a, b) => {...}, the code within the {} is not called until the respective event is actually raised (i.e., clicking the respective label).
Related
I've ran into quite an annoying problem.
I've got these global variables (for keeping the name etc)
List<object> NumeriekVakken = new List<object>();
List<decimal> bedragenLijst = new List<decimal>();
List<string> namenlijstVanNumericFields = new List<string>();
List<string> namenLijst = new List<string>();
Afterwards I have a function that makes NumericUpDowns, depending on the number of records in the db.
The function looks like this:
private void InitializeComponentControlArrayKnoppenTextboxenEnLabels()
{
foreach (DataRow dr in blCategorie.getAlleCategorieenMetLimieten())
{
double limiet = (double) dr.Field<double>("maximumBedrag");
NumericUpDown numeriekVak = new NumericUpDown();
numeriekVak.Name = "numeriekvak" + i;
numeriekVak.Width = 100;
numeriekVak.Maximum = 30000;
numeriekVak.Minimum = 0;
numeriekVak.Increment = 10;
numeriekVak.Value = Convert.ToDecimal(limiet);
numeriekVak.Location = new Point(250, beginhoogte + verhogenMet);
this.Controls.Add(numeriekVak);
NumeriekVakken.Add(numeriekVak);
bedragenLijst.Add(numeriekVak.Value);
namenlijstVanNumericFields.Add(numeriekVak.Name);
namenLijst.Add(categorie);
//to make unique names for my Numerics etc.
i++;
counter++;
//click event aanmaken
button.Click += new EventHandler(buttonWijzig_Click);
}
}
And in the ending I want to update a record whenever the numericUpDown is changed by the user (by clicking on the numericupdown or changing the numbers)
private void buttonWijzig_Click(object sender, EventArgs e)
{
Button knop = (Button)sender;
NumericUpDown numeriekvak = (NumericUpDown)sender;
for (int i = 0; i < counter; i++)
{
if (knop.Name == "knop" + i)
{
int id = i, maximumBedrag = 0;
if (namenlijstVanNumericFields[i] == "numeriekvak" + i)
{
// update limit
DBManager.LimietRow limiet = new DBManager.LimietDataTable().NewLimietRow();
maximumBedrag = Convert.ToInt32(numeriekvak.Value);
blLimiet.updateLimiet(id, maximumBedrag);
}
labelBevestigingLimiet.Text = "Limiet " + namenLijst[i].ToString() + " is succesvol gewijzigd naar " + maximumBedrag + "€";
//stopping of loop if right button is found.
break;
}
}
}
But evertime I run this I get the same problem..
"Can't convert the object from the type System.Windows.Forms.Button to the type
System.Windows.Forms.NumericUpDown"
How can I fix this and update the record depending on the new number thats filled in on the NumericUpDown? I can't get this working, I've got a feeling it has to do with the sender thats not working good..
Thanks for all the help!
Yenthe
You have assigned the event handler buttonWijzig_Click to a control button.
This control is not defined anywhere in your code above.
(By the way, you assign the method at the same control for every loop)
I think you want to assign your event handler to every NumericUpDown created in the loop as
numeriekVak.Click += new EventHandler(buttonWijzig_Click);
Of course the event handler now receives a NumericUpDown control in the sender argument and not a button, so the code of the event handler should be changed accordingly
private void buttonWijzig_Click(object sender, EventArgs e)
{
NumericUpDown numeriekvak = (NumericUpDown)sender;
int id = 0, maximumBedrag = 0;
// The ID could be extracted from the control name starting at 11 position
id = Convert.ToInt32(numeriekvak.Name.Substring(11));
// update limit
DBManager.LimietRow limiet = new DBManager.LimietDataTable().NewLimietRow();
maximumBedrag = Convert.ToInt32(numeriekvak.Value);
blLimiet.updateLimiet(id, maximumBedrag);
// The control name is already available, no need to use the list to retrieve it
labelBevestigingLimiet.Text = "Limiet " + numeriekVak.Name + " is succesvol gewijzigd naar " + maximumBedrag + "€";
}
However, let me say that for your stated purpose:
And in the ending I want to update a record whenever the numericUpDown
is changed by the user (by clicking on the numericupdown or changing
the numbers)
it is better to use the ValueChanged event because this will be fired also when the user changes the value manually and not with up/down buttons. The code above will fit as well for the ValueChanged event
EDIT
Based on your comment below then the assignment of the event handler goes back to the button (there is no code to create the button, so I have assumed that you have followed the same naming convention as for yours NumericUpDown), but insted of using a list to keep track of your NumericUpDown I would use a Dictionary<int, NumericUpDown> where the integer is the id needed to retrieve the corresponding NumericUpDown from the button name.
In declaration change
Dictionary<int, NumericUpDown> NumeriekVakken = new Dictionary<int, NumericUpDown> ();
In Initialization inside InitializeComponentControlArrayKnoppenTextboxenEnLabels change
namenlijstVanNumericFields.Add(i, numeriekVak);
In button click code
private void buttonWijzig_Click(object sender, EventArgs e)
{
Button knop = sender as Button;
int id = 0, maximumBedrag = 0;
// The ID could be extracted from the control name starting at 4th position
id = Convert.ToInt32(knop.Name.Substring(4));
// The ID is the key to find the corresponding NUmericUpDown in the dictionary
NumericUpDown numeriekvak = NumeriekVakken[id];
// update limit
DBManager.LimietRow limiet = new DBManager.LimietDataTable().NewLimietRow();
maximumBedrag = Convert.ToInt32(numeriekvak.Value);
blLimiet.updateLimiet(id, maximumBedrag);
// The control name is already available, no need to use the list to retrieve it
labelBevestigingLimiet.Text = "Limiet " + numeriekVak.Name + " is succesvol gewijzigd naar " + maximumBedrag + "€";
}
The problem is: since you event is fired by a button, you cannot convert the sender to numeric updown.
I see you have only one button and it's not declared in you code.
I assume it's in the form, right???
Did you mean to add that click event many times to a single button???
Or did you mean to add events to each NumericUpDown???
If it's the second option, you should add the click event to each numeric updown.
See this line in your code.
//click event aanmaken
button.Click += new EventHandler(buttonWijzig_Click);
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if(e.RowIndex >= 0) {
DataGridViewRow row = this.dataGridView1.Rows[e.RowIndex];
iD_supplierNumericUpDown.Value = row.Cells["ID"].Selected(); // this is the problem
nSupplierTextBox.Text = row.Cells["NSupplier"].Value.ToString();
e_mailTextBox.Text = row.Cells["E_mailTextBox"].Value.ToString();
phoneTextBox.Text = row.Cells["Phone"].Value.ToString();
}
I've an array of textboxes that generates run-time a variable number of textboxes such the value of a textbox already created into the form.
int n;
TextBox[] tb;
public void AggiungiArmoniche()
{
n = int.Parse(textBox4.Text);
tb = new
TextBox[n];
for (int i = 1; i < tb.Length; i++)
{
tb[i] = new TextBox();
tb[i].Name = "textBox" + i;
tb[i].Location = new Point(100 *i, 163);
tb[i].Size = new Size(48, 20);
tb[i].KeyPress += System.Windows.Forms.KeyPressEventHandler(textBoxP_KeyPress);
groupBox1.Controls.Add(tb[i]);
}
}
private void textBoxP_KeyPress(object sender, KeyPressEventArgs e)
{
// statements of the event
}
When I move to the line in which i associate the event to the event-handler it gives the error it isn't a valid construct in the contest" (in particular in the word keypresseventhandler)
is there a syntactical error in the association?
Remove the KeyPressEventHandler and add the event handler like so
tb[i].KeyPress += textBoxP_KeyPress;
new
tb[i].KeyPress += new System.Windows.Forms.KeyPressEventHandler(textBoxP_KeyPress);
I created an array of of TextBoxes and an array of Labels. When the information is updated in the TextBox I want it to change the Labels. How would I be able to do this? Below is piece of my code. I have not created the EvenHandler that I think is the part I need help with. All in C# using windows application form.
textBoxes = new TextBox[value];
labels = new Label[value];
for (int i = 1; i < value; i++)
{
textBoxes[i] = new TextBox();
textBoxes[i].Location = new Point(30, ToBox.Bottom + (i * 43));
labels[i] = new Label();
labels[i].Location = new Point(TopBox3[i].Width + 140, TopBox3[i].Top +3);
textboxes[i].ValueChanged += new EventHandler(this.TextBox_ValueChanged) ;
this.Controls.Add(labels[i]);
this.Controls.Add(textBoxes[i]);
}
You can remember the index of the TextBox in the Tag property
textBoxes[i].Tag = i;
and then use this value in your eventhandler to get the corresponding label (assuming that you hold the labels array as a local variable)
protected void TextBox_ValueChanged(object sender, EventArgs e)
{
TextBox textbox = sender as TextBox;
if(textbox==null)
return;
int index = Convert.ToInt32(textbox.Tag);
if(index >= 0 && index < this.labels.Length)
{
Label label = this.labels[index];
/* ... */
}
}
You should write something like this:
private void textBox1_ValueChanged(object sender, EventArgs e)
{
TextBox changedTxt = sender as TextBox;
for (int i = 1; i < value; i++)
if (textBoxes[i] == changedTxt)
{
Label lblToChange = labeld[i];
lblToChange.Text = changedTxt.Text;
break;
}
}
In the method the TextBox whose text has changed is passed as "sender". You look into your array for it, so you identify the index "i" which can be used to access the corresponding Label and to set its text.
BTW as Tim said, the event is TextChanged, not ValueChanged. Furthermore be aware that the event is triggered for every change in the text, i.e. as soon as you press a key the label will be updated. If you prefer to update your labels only when the user has finished to enter its text Leave is the event you should use.
I'm creating an array of controls and adding them to the form, and setting their events to a function that receives the index of the clicked button using a lambda expression (b.Click += (sender, e) => myClick(i);).
But the problem is... Whichever you click on, you receive the index 100, not the real index of the button! What is the problem here?
namespace testArrayOfControls
{
public partial class Form1 : Form
{
Button[] buttons;
public Form1()
{
InitializeComponent();
buttons = new Button[100];
for (int i = 0; i < 100; i++)
{
buttons[i] = new Button();
buttons[i].SetBounds(i % 10 * 50, i / 10 * 50, 50, 50);
buttons[i].Click += (sender, e) => myClick(i);
this.Controls.Add(buttons[i]);
}
}
private void myClick(int i)
{
MessageBox.Show(i.ToString());
}
}
}
The problem is that you create closure over the loop variable i. You need to make a local (inside the for loop) copy of it before passing it to the event handler.
for (int i = 0; i < 100; i++)
{
var index = i; // YOU NEED TO DO THIS
buttons[i] = new Button();
buttons[i].SetBounds(i % 10 * 50, i / 10 * 50, 50, 50);
buttons[i].Click += (sender, e) => myClick(index); // THIS SOLVES THE PROBLEM
this.Controls.Add(buttons[i]);
}
Explanation
You are defining a function like this:
(sender, e) => myClick(i)
This function (which will run at some point in the future, when the button is clicked) includes a reference to i. The way, this works is that it will use the value of i at the time when the click occurs, not at the time the function is defined.
By that time, clearly the value of i will have become 100.
The solution works because it makes the function take a reference to the variable index instead of i. index differs from i in that i is one variable whose value changes, while index is a name we use for 100 different variables (one for each loop iteration), the value of which remains constant.
The problem is to do with modified closures. Here is an excellent explanation of the subject by #Jon Skeet.
int index = i;
buttons[i].Click += (sender, e) => myClick(index);
Try that.
Unfortunately closures (in ur case the variable i) doesn't work the way they should be in C#.
Replace
b.Click += (sender, e) => myClick(i);
with
Action<int,Button> act = (index,b) => { b.click += (sender, e) => myClick(index) }
act(i,b);
So in my program i created a struct with a button and a number value... like this
struct box
{
public int numberValue;
public Button button;
}
I then made a 2D array of this struct
box[,] boxes = new box[20, 20];
Now what i did was make 400 buttons and assigned them to each index of the array... like this
private void createBoxes()
{
int positionX;
int positionY;
for (int i = 0; i < 20; i++)
{
for (int j = 0; j < 20; j++)
{
positionX = 20 + (25 * i);
positionY = 20 + (25 * j);
boxes[i, j].button = new System.Windows.Forms.Button();
boxes[i, j].button.Location = new System.Drawing.Point(positionX,positionY);
boxes[i, j].button.Size = new System.Drawing.Size(25, 25);
this.Controls.Add(boxes[i, j].button);
boxes[i, j].button.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
boxes[i, j].button.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
boxes[i, j].button.Visible = true;
boxes[i, j].button.Name = "button";
boxes[i, j].button.Click += new EventHandler(buttonClick);
}
}
}
Now when i make the event handler i want to send "boxes[i,j]" not just "boxes[i,j].button" is there anyway to do this?
Short of defining your own anonymous event handler, there's an easy way to do what you want:
boxes[i, j].button.Tag = boxes[i, j];
Then later:
private void buttonClick(object sender, EventArgs e)
{
var box = ((Button)sender).Tag as box;
}
This can be solved via an anonymous event handler.
var box = boxes[i, j]; // You must use a new variable within this scope
box.button.Click += (obj, args) => buttonClick(box, args);
This is the quickest solution with the least code. Just be aware that anonymous event handlers are notorious for hidden gotchas, and the need to assign a new box variable is an example. The following code will run, but no matter which button you press, the last-assigned values of i and j would be used within the handler.
boxes[i,j].button.Click += (obj, args) => buttonClick(boxes[i,j], args);
No, this is not possible. The individual button control is the one that raises the event, thus it is the object referenced by the sender parameter. The array that contains the button control is irrelevant.
This behavior is by-design. If you wanted to change a property of the button in response to the user clicking on it, it would be impossible to do unless you knew which individual button was clicked. Having only a reference to the array that contains all of the buttons would not provide sufficient information about the individual button that was clicked.