Sending a variable in a C# EventHandler? - c#

How can I send a variable with a newly created EventHandler?
The code I have is something like:
for (int i = 0; i < 5; i++)
{
Button buttonX = new Button();
buttonX.Location = new Point(0, 0 + offset);
buttonX.Size = new Size(310, 48);
buttonX.Click += new EventHandler(buttonClick);
}
private void buttonClick(object sender, EventArgs e)
{
MessageBox.Show();
}
How can I can make it like
buttonX.Click += new EventHandler(buttonClick , i);
private void buttonClick(object sender, EventArgs e, int i)
{
MessageBox.Show(i.toString());
}

Closures are grand. You can use lambda notation:
buttonX.Click += (sender, e) => buttonClick(sender, e, i);
or anonymous delegate notation:
buttonX.Click += delegate (object sender, EventArgs e) { buttonClick(sender, e, i); };
However, you're going to have trouble if you capture the loop variable.
Instead, do
for (int i = 0; i < 5; i++)
{
Button buttonX = new Button();
buttonX.Location = new Point(0, 0 + offset);
buttonX.Size = new Size(310, 48);
var i_copy = i;
buttonX.Click += (sender, e) => buttonClick(sender, e, i_copy);
}

Given your scenario, just utilize the Tag property; don't mix the solution with the Click event; you don't need to "pass" i every time to the event; just tag it to the control since it's always the same; that way you don't have to worry about closure.
Change your code to this:
for (int i = 0; i < 5; i++)
{
Button buttonX = new Button();
buttonX.Location = new Point(0, 0 + offset);
buttonX.Size = new Size(310, 48);
buttonX.Click += new EventHandler(buttonClick);
buttonX.Tag = i;
}
private void buttonClick(object sender, EventArgs e)
{
MessageBox.Show(((Button)sender).Tag.ToString());
}

You would create a class derived from EventArgs that carries the additional data points that you want.
http://msdn.microsoft.com/en-us/library/system.eventargs.aspx
So instead of just using EventArgs you would create an EventArgs for the event you are raing.
public class ButtonClickedEventArgs : EventArgs
{
public int EventInteger { get; private set; }
public ButtonClickedEventArgs(int i)
{
EventInteger = i;
}
}
And then when you raise the event you would create the ButtonClickedEventArgs class and pass that with the EventHandler.

You can't do that, but what you can do is use an intermediate lambda:
for (int i = 0; i < 5; i++)
{
int j = i; // Need to do this to fix closure issue
Button buttonX = new Button();
buttonX.Location = new Point(0, 0 + offset);
buttonX.Size = new Size(310, 48);
buttonX.Click += (sender, e) => {
buttonClick(sender, e, j);
};
}
private void buttonClick(object sender, EventArgs e, int i)
{
MessageBox.Show(i.toString());
}

Related

Labels don't move using Mouse events

I created 5 labels using a for. Inside the for I declared the properties of the labels, such as location, size, color, etc. and the possibility to move the labels using the mouse events:
private void Form1_Load(object sender, EventArgs e)
{
int alto = 100;
int ancho = 65;
for (byte fila = 0; fila < 5; fila++)
{
Label letras = new Label();
letras.Height = alto;
letras.Width = ancho;
letras.BackColor = Color.Blue;
letras.ForeColor = Color.AntiqueWhite;
letras.Font = new Font("Arial", 60);
letras.TextAlign = ContentAlignment.MiddleCenter;
letras.BorderStyle = BorderStyle.FixedSingle;
letras.Left = 200 + (ancho + 20) * fila;
letras.Top = 60;
letras.Text = null;
letras.MouseDown += new MouseEventHandler(this.letras_MouseDown);
letras.MouseMove += new MouseEventHandler(this.letras_MouseMove);
letras.MouseUp += new MouseEventHandler(this.letras_MouseUp);
this.Controls.Add(letras);
}
}
Later in the code, I create the events which will make the labels move.
private void letras_MouseDown(object sender, MouseEventArgs e)
{
mousedown = true;
}
private void letras_MouseMove(object sender, MouseEventArgs e)
{
if (mousedown)
{
letras.Location = new Point(letras.Location.X + e.Location.X, letras.Location.Y + e.Location.Y);
}
}
private void letras_MouseUp(object sender, MouseEventArgs e)
{
mousedown = false;
}
but it isn't working. Any ideas? I think that could be an Indentation problem, but I'm not sure.
The sender argument received by the letras_MouseMove method is the Label so the first thing to do is perform a cast to a usable Label object. Then simply test the static Control.MouseButtons property to determine if the Left button is currently active. You calculated the offset correctly. I believe that all you really needed was to have the correct Label object. (Of course I tested it)
Some of the code you posted is not necessary and should be removed. This shows a minimal working example.
public partial class MainForm : Form
{
public MainForm() => InitializeComponent();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// This code stays the same
int alto = 100;
int ancho = 65;
for (byte fila = 0; fila < 5; fila++)
{
Label letras = new Label();
letras.Height = alto;
letras.Width = ancho;
letras.BackColor = Color.Blue;
letras.ForeColor = Color.AntiqueWhite;
letras.Font = new Font("Arial", 60);
letras.TextAlign = ContentAlignment.MiddleCenter;
letras.BorderStyle = BorderStyle.FixedSingle;
letras.Left = 200 + (ancho + 20) * fila;
letras.Top = 60;
letras.Text = null;
letras.MouseMove += new MouseEventHandler(this.letras_MouseMove);
this.Controls.Add(letras);
}
}
// This code is different
private void letras_MouseMove(object sender, MouseEventArgs e)
{
Label letra = (Label)sender;
if(MouseButtons == MouseButtons.Left)
{
// Here, your original calculation works OK once you have the correct Label object
letra.Location = new Point(letra.Location.X + e.Location.X, letra.Location.Y + e.Location.Y);
}
}
}
Haven't tested but replace:
private void Form1_Load(object sender, EventArgs e)
{
int alto = 100;
int ancho = 65;
for (byte fila = 0; fila < 5; fila++)
{
letras = new Label();
With:
Label[] labels = Array.Empty<Label>();
private void Form1_Load(object sender, EventArgs e)
{
int alto = 100;
int ancho = 65;
for (byte fila = 0; fila < 5; fila++)
{
labels[fila] = new Label();
And replace every letras in the file with labels[(number)].

c# Picturebox Array Selection

I have created an array of picture boxes and an event for when one is clicked.
public void TicTac_Load(object sender, EventArgs e)
{
PictureBox[] PBox = new PictureBox[9];
PBox[0] = this.pictureBox1;
PBox[1] = this.pictureBox2;
PBox[2] = this.pictureBox3;
PBox[3] = this.pictureBox4;
PBox[4] = this.pictureBox5;
PBox[5] = this.pictureBox6;
PBox[6] = this.pictureBox7;
PBox[7] = this.pictureBox8;
PBox[8] = this.pictureBox9;
for (int i = 0; i < 9; i++)
{
PBox[i].Click += new System.EventHandler(PBoxes_Click);
}
}
public void PBoxes_Click(object sender, EventArgs e)
{
PictureBox myPictureBox = sender as PictureBox;
//if(Pbox[1].click){
//^^ Looking for something like this
}
My question is how can I tell which one of my pictureboxes has been clicked as i am unable to access any of them. I would just like to be able to tell which has been clicked inside the method instead of creating many.
pictureBox1_Click(object sender, EventArgs e)
Like Events
There a multiple ways to solve the issue.
You could cast sender to the correct type (here PictureBox):
public void TicTac_Load(object sender, EventArgs e)
{
PictureBox[] PBox = new PictureBox[9];
PBox[0] = this.pictureBox1;
PBox[1] = this.pictureBox2;
PBox[2] = this.pictureBox3;
PBox[3] = this.pictureBox4;
PBox[4] = this.pictureBox5;
PBox[5] = this.pictureBox6;
PBox[6] = this.pictureBox7;
PBox[7] = this.pictureBox8;
PBox[8] = this.pictureBox9;
for (int i = 0; i < 9; i++)
{
PBox[i].Click += new System.EventHandler(PBoxes_Click);
}
}
public void PBoxes_Click(object sender, EventArgs e)
{
PictureBox myPictureBox = sender as PictureBox;
}
Alternatively (less-recommended), you could move PBox to a class-level array:
PictureBox[] PBox = new PictureBox[9];
public void TicTac_Load(object sender, EventArgs e)
{
PBox[0] = this.pictureBox1;
PBox[1] = this.pictureBox2;
PBox[2] = this.pictureBox3;
PBox[3] = this.pictureBox4;
PBox[4] = this.pictureBox5;
PBox[5] = this.pictureBox6;
PBox[6] = this.pictureBox7;
PBox[7] = this.pictureBox8;
PBox[8] = this.pictureBox9;
for (int i = 0; i < 9; i++)
{
PBox[i].Click += new System.EventHandler(PBoxes_Click);
}
}
public void PBoxes_Click(object sender, EventArgs e)
{
PictureBox myPictureBox = PBox[PBox.indexOf(sender)];
}

dynamically adding buttons and button objects in panel

private void button1_Click(object sender, EventArgs e)
{
string a = textBox1.Text;
int h = Convert.ToInt32(a);
for (int i = 0; i <= h; i++)
{
buttonArray[i] = new Button();
buttonArray[i].Size = new Size(60, 23);
buttonArray[i].Location = new Point(40,20);
panel1.Controls.Add(buttonArray[i]);
}
}
my task is if user enter 3 in text box. 3 buttons should be created dynamically and added to panel how to do that?????? i am using button array please suggest me
For example you can use this.
private void button1_Click(object sender, EventArgs e)
{
panel1.Controls.Clear();
string a = textBox1.Text;
int h = Convert.ToInt32(a);
for (int i = 0; i <= h; i++)
{
var btn = new Button {Size = new Size(60, 23), Dock=DockStyle.Left, Text=h.ToString() };
btn.Click+= delegate(object sender, EventArgs e) { //your commands };
panel1.Controls.Add(btn);
}
}
In this post already answered a similar question:
private void button1_Click(object sender, EventArgs e)
{
string a = textBox1.Text;
int h = Convert.ToInt32(a);
for (int i = 0; i <= h; i++)
{
var b = new Button { Size = new Size(60, 23), Location = new Point(4 + i * 57, 20), Text = string.Format("button{0}", i) };
b.Click += b_Click;
panel1.Controls.Add(b);
}
}
void b_Click(object sender, EventArgs e)
{
throw new NotImplementedException();
}
it is desirable to test the validator to always have been a number
string a = textBox1.Text;
May be using a List is more situable?
List<Button>Buttons=new List<Buttons>();
private void button1_Click(object sender, EventArgs e)
{
Buttons.Clear();
string a = textBox1.Text;
//here should be checking if "a" is digit and is not empty
int h = Convert.ToInt32(a);
for (int i = 0; i <= h; i++)
{
Button btn=new Button();
btn.Parent=panel1;
btn.Size=new Size(60, 23);
btn.Location = new Point(40,5+25*i); //arrange verically
btn.Text = "Button "+i.ToString();
btn.Click+=btn_Click;
btn.Tag="Some Value you want to restore after button click";
Buttons.Add(btn)
}
}

Add Arrays of C# button Click in code in Run Time

To create Button and its click event in run time I use:
Button b = new Button();
b.Name = "btn1";
b.Click += btn1_Click;
But now I have an array of Buttons to create in run time; how to set each button's event - I cannot interpolate because it's not a string.
Button[] b = new Button(Count);
for (int i=0; i < Count; i++)
{
b[i] = new Button();
b[i].Name = "btn" + i;
b[i].Click += ??????
}
what should I do for "?????"
Option 1:
You can pass an lambda function, and create the handler based on the buttons index in the array like this:
for (int i=0; i < Count; i++)
{
b[i] = new Button();
b[i].Name = "btn" + i;
b[i].Click += (sender, args) =>
{
// your code
}
}
Option 2:
You can pass an anonymus delegate:
b[i].Click += delegate (sender, args) {
// your code
};
Option 3:
You can specify a handler function:
b[i].Click += YourHandlerFunction
// ....
// The handler signature also has to have the correct signature
void YourHandlerFunction(object sender, ButtonEventArgs args)
{
// your code
}
You can bind all buttons to the same event, so put the line like b[i].Click += button_Click;.
Then inside the button_Click event you can differentiate between the buttons, and take the proper actions.
For example:
public void button_Click(object sender, ButtonEventArgs e)
{
if( sender == b[0] )
{
//do what is appropriate for the first button
}
...
}
It depends on what you want to do! If you want to have the same method called for all clicks, do this:
Button[] b = new Button[Count];
for (int i=0; i < Count; i++)
{
b[i] = new Button();
b[i].Name = "btn" + i;
b[i].Click += OnClick
}
private void OnClick(object sender, RoutedEventArgs e)
{
// do something
}
If you want to do something different for each button, e.g. depending on the index, you can do something like this:
Button[] b = new Button[Count];
for (int i=0; i < Count; i++)
{
b[i] = new Button();
b[i].Name = "btn" + i;
b[i].Click += (s, e) => { /*do something*/ };
}

Changing properties of the sender object without knowing its name

I am trying to change the BackColor of a PictureBox in a grid. The PictureBox is part of an array and the array has a chared event handler. I am having difficulty changing different PictureBox's depending on which one is clicked.
This is what I have so far:
private PictureBox[,] GameGrid = new PictureBox[20, 20];
public frmGame()
{
int x = 10;
int y = 10;
for (int i = 0; i < 20; i++)
{
for (int j = 0; j < 20; j++)
{
GameGrid[i, j] = new System.Windows.Forms.PictureBox();
setUpPicBox(x, y, i, j);
x += 11;
}
y += 11;
x = 10;
}
InitializeComponent();
}
public void setUpPicBox(int x, int y, int i, int j)
{
this.GameGrid[i, j].Location = new System.Drawing.Point(x, y);
this.GameGrid[i, j].Size = new System.Drawing.Size(10, 10);
this.GameGrid[i, j].BackColor = Color.Black;
this.GameGrid[i, j].Name = "btnGrid" + i + "-" + j;
this.GameGrid[i, j].Visible = true;
this.GameGrid[i, j].CreateGraphics();
this.GameGrid[i, j].Click += new System.EventHandler(this.picturebox_Click);
this.Controls.Add(GameGrid[i, j]);
}
private void picturebox_Click(object sender, EventArgs e)
{
}</code>
Any help would be appreciated
The event handler's sender parameter is the instance that raised the event. Here it is the PictureBox instance that the user has clicked. If you want to change its BackColor, you just cast the sender object to correct type and set the new color.
private void picturebox_Click(object sender, EventArgs e)
{
var pictureBox = sender as PictureBox;
if (pictureBox != null) {
pictureBox.BackColor = Color.Blue;
}
}
On your event handler, sender contains the object that caused the event handler to fire. So by casting it to the correct type, we can then access all the properties as per this example:
private void picturebox_Click(object sender, EventArgs e)
{
PictureBox pic = (PictureBox)sender;
MessageBox.Show(pic.Name);
}
Note: code untested, not got access to VS to test

Categories