EventHandlers and the sender - c#

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.

Related

How to pass an index of a button to an EventHandler?

Good to know is that I just started programming, so go easy on me ;)
In my program I make a board consisting of several buttons (btn[i, j]), which I create by using two for loops. These buttons are given a coordinate pair/index [i, j], then I pass this to a 2d array called valueBtn and I give that coordinate pair a value on the corresponding index.
public void board(object obj, EventArgs ea)
{
int n = 6;
Button[,] btn = new Button[n, n];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
int p = 60 * i + 100;
int q = 60 * j + 100;
btn[i, j] = new Button();
btn[i, j].Location = new Point(p, q);
btn[i, j].Size = new Size(60, 60);
int[,] valueBtn = new int[n, n];
valueBtn[i, j] = 0;
this.Controls.Add(btn[i, j]);
}
}
btn[i, j].Click += btnPress;
}
Next I have a new method btnPress which is linked to the EventHandler btn[i, j].Click. The intention is that in this method I find out which button has been pressed, and which coordinates/index belongs to this so that I can find the corresponding value in the 2d array valueBtn and eventually draw this value in a new function drawValue.
public void btnPress(object sender, EventArgs ea)
{
Button pressedBtn = sender as Button;
// Here I want to know which button is pressed and the index [i,j] of the button
// so that I can find the value that belongs to the button in the 2d valueBtn array
.
.
.
this.Paint += drawValue;
}
I've tried a lot with references to the EventHandler, but I just can't figure it out.
Thank you very much in advance for your time and help!
You can set the Text property of each button like btn[i, j].Text = some i j combination. and then get it in btnPress method like this string s = (sender as Button).Text;. You can also use Tag property, so basically concept is same.

I am having trouble with windows form assignment puzzle slide

I am trying to set the array for all 9 buttons to move around to the blank tile.
for example
this: to this
1 2 3 1 2 3
4 5 6 4 5 6
7 8 7 8
The teacher claims she, "Don’t forget that you will need to cast sender to a Button" this is so I can be able to swap the tiles with the blank tile as shown above. How do I do that?
Then when I reset everything back to its original location in the reset event handler, "I get Object reference not set to an instance of an object." Do you know how I fix that error? This is because it says the same syntax error when I try to cast sender to a Button.
Here is the following code that I hope you can fix.
public partial class frmSlide : Form
{
/*
* frmSlide -constructor
*
*/
Button[,] arr = new Button[3, 3];
public frmSlide()
{
InitializeComponent();
}
/*
* allNumberButton_Click - click event handler for allNumberButton
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void frmSlide_Load(object sender, EventArgs e)
{
Button b;
b = new Button();
b.Location = new Point(79, 104);
b.Width = 40;
b.Height = 40;
b.Text = "";
b.TextAlign = ContentAlignment.MiddleCenter;
b.Font = new Font(FontFamily.GenericSerif, (float)15.0);
b.Click += new System.EventHandler(this.allNumberButton_Click);
this.Controls.Add(b);
}
private void InitTwoDim(Button[,] arr)
{
for (int i = 0; i < arr.GetLength(0); i++)
{
for (int j = 0; j < arr.GetLength(1); j++)
{
Button b;
b = new Button();
b.Location = new Point(79, 104);
b.Width = 40;
b.Height = 40;
b.Text = "1";
b.TextAlign = ContentAlignment.MiddleCenter;
b.Font = new Font(FontFamily.GenericSerif, (float)15.0);
b.Click += new System.EventHandler(this.allNumberButton_Click);
this.Controls.Add(b);
arr[i, j] = b;
}
}
}
private void allNumberButton_Click(object sender, EventArgs e)
// this is when I tried to cast button to sender to swap text but nothing happened
{
Button btn8 = (Button)sender;
Button btnBlank = (Button)sender;
string tmp;
tmp = btn8.Text;
btn8.Text = btnBlank.Text;
btnBlank.Text = tmp;
}
/*
* btnReset_Click - click event handler
* .text
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btnReset_Click(object sender, EventArgs e)
{
//This is where I tried to reset the button at its original location but got an error about'
//Object reference not set to an instance of an object.'
arr[0, 0].Text = "1";
arr[0, 1].Text = "2";
arr[0, 2].Text = "3";
arr[0, 3].Text = "4";
arr[1, 1].Text = "5";
arr[1, 2].Text = "6";
arr[1, 3].Text = "7";
arr[2, 1].Text = "8";
arr[2, 2].Text = " ";
}
/*
* btn1_Click -click event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btn1_Click(object sender, EventArgs e)
{
}
/*
* btn8_click -click event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btn8_Click(object sender, EventArgs e)
{
btn8.Text = btnBlank.Text;
btnBlank.Text = btn8.Text;
}
/*
* btnExit_Click -click event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btnExit_Click(object sender, EventArgs e)
{
this.Close();
}
/*
* frmSlide_Load- Load event handler
*
* Parameter: sender -who sent the event
* Parameter: e -info about the event
* Return: nothing
*/
private void btnStart_Click(object sender, EventArgs e)
{
}
}
Can you help me try to swap the text with the blank tile and reset all tiles back to its location? This is because im basically trying to create a slide puzzle using a two dimensional array for the 9 buttons being arranged with the blank button. This is for c#.
If you run your application in Debug (F5) (rather, with an attached debugger), you will generally see and be prompted on which line in your source is giving you the exception. (i.e. uses debug symbols to essentially map back to your source code)
The line arr[0,0].Text = 1 (and every other reference to arr[<column>, <row>] usage in that scope) is going to throw an object reference exception if there are no instances of Button in arr.
Note that the line Button[,] arr = new Button[3, 3]; dimension the array (arr) by defining the length and type of that 2 Dimension array of type Button, but it doesn't actually create\construct button instances.
So, to resolve this object reference exception, you need to ensure those button instances exist before attempting to use the .Text property.
You can do that by calling to InitTwoDim and passing arr in your form load event handler: InitTwoDim(this.arr); But, this in this.arr is unnecessary code, and passing the argument is also unnecessary.
The following code will refactor that method call, and move it to the class constructor.
public frmSlide()
{
InitializeComponent();
InitializeSliderButtons();
}
void InitializeSliderButtons() {
int i = 1;
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++, i++) {
arr[c, r] = GetNewButton(i.ToString(), c * defaultButtonWidth, r * defaultButtonHeight);
this.Controls.Add(arr[c, r]);
}
}
}
int defaultButtonWidth = 40;
int defaultButtonHeight = 40;
Button GetNewButton(string btnText, int offsetX, int offsetY) {
var b = new Button()
{
Location = new Point(79 + offsetX, 104 + offsetY),
Width = defaultButtonWidth,
Height = defaultButtonHeight,
Text = btnText,
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font(FontFamily.GenericSerif, (float)15.0)
};
b.Click += new System.EventHandler(this.allNumberButton_Click);
return b;
}
Noteworthy
The click event handler is assumed to be the same for all buttons here.
a 2D array is also 0 based indexed array. Currently, your reset function is attempting to index with 3 (like arr[0,3]) that will cause a different exception (out of range).
These Button instances should be added to the Form and their Button.Location assign, or Point instance, should be handled; I'm defining and including offsetX and offsetY as arguments on the helper to ensure the buttons do not stack at the same location.
I defined defaultButtonWidth and defaultButtonHeight for maintainability. They should be defined with static readonly keywords (you should do that); but, I didn't want to introduce concepts you would have to explain on.

How to dynamically create buttons and to add different event handler?

I created a function that appends buttons in a form. Buttons when clicked supposed to open different files. File paths are in listbox1. I want to add a click event for every button I append. One button should open one file from listbox1.
The part with appending buttons works, but I can't add a different event for each of them only one.
This is my code. It adds the event to every button but only the last one.
PlaySong is a function that plays ".mp3" file. That works.
Can someone help me?
int i = 0;
private void Load_Songs()
{
List<string> url = new List<string>();
url = listBox1.Items.Cast<String>().ToList();
int p = 5;
for (int j = 0; j < listBox1.Items.Count; j++)
{
EventHandler klik = new EventHandler(Playing);
Song_Data titl = new Song_Data(url[j]);
Button n = new Button
{
Text = titl.Title,
Location = new Point(0, p + 20),
Width = ClientRectangle.Width / 3,
FlatStyle = FlatStyle.Flat
};
p += 20;
n.Click += klik;
List_Artist.Controls.Add(n);
i++;
}
}
private void Playing(object sender, EventArgs e)
{
PlaySong(listBox1.Items[i].ToString());
}
You don't need many event handlers, just store the index to the button's Tag in your loop and then use it to find which index you should use to choose from listbox:
Button n = new Button
{
Text = titl.Title,
Location = new Point(0, p + 20),
Width = ClientRectangle.Width / 3,
FlatStyle = FlatStyle.Flat,
Tag = j
};
Then in your handler:
private void Playing(object sender, EventArgs e)
{
int i= (int)((Button)sender).Tag;
PlaySong(listBox1.Items[i].ToString());
}
to use different handler for each button you can use anonymous event handler, but won't solve your problem:
n.Click += (s, ev) =>
{
//code when button clicked
};

Associate a keypress event with a event handler in a array of textboxes

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);

Create button during runtime in C#.net?

I know how to create button during runtime.
Button button1 = new Button();
button1.Location = new Point(20,10);
button1.Text = "Click Me";
// adding to groupBox1
groupBox1.Controls.Add(button1);
But the problem is i want to add multiple buttons like this..
for(int i = 1; i < 30; i++) {
Button button[i] = new Button();
// Button customization here...
...
groupBox1.Controls.Add(button[i]);
}
The code above is false code. How can I make this happen true in C#.net? i want to create multiple buttons with button name, button1, button2, button3, button4, .... button30;
You can't declare extra variables at execution time in C# - but you really don't want to anyway, as you wouldn't be able to access them dynamically afterwards. Just create an array:
// buttons would be declared as Button[] as a member variable
buttons = new Button[30];
for(int i = 0; i < buttons.Length; i++) {
buttons[i] = new Button();
// Button customization here...
...
groupBox1.Controls.Add(buttons[i]);
}
Alternatively, use a List<Button>, which will certainly be more convenient if you don't know how many buttons you need beforehand. (See the obligatory "arrays considered somewhat harmful" blog post.)
Of course, if you don't actually need to get at the buttons later, don't bother assigning them to anything visible outside the loop:
for(int i = 0; i < 30; i++) {
Button button = new Button();
// Button customization here...
...
groupBox1.Controls.Add(button);
}
You need to think about what information you need access to when... and how you want to access it. If you logically have a collection of buttons, you should use a collection type variable (like a list or an array).
Frankly I think it's one of the curses of the VS designers that you end up with horrible names such as "groupBox1" which carry no information beyond what's already in the type declaration, and encourage developers to think of collections of controls via individually-named variables. That's just me being grumpy though :)
Try this
for(int i = 1; i < 30; i++) {
Button button = new Button();
// Button customization here...
button.Name = "Button" + i.ToString();
groupBox1.Controls.Add(button);
}
You seem like you're almost on the right track:
// in form class
Button[] m_newButtons = new Button[30];
// in your trigger function
for(int i = 0; i < 30; ++i)
{
m_newButtons[i] = new Button();
// ...
groupBox1.Controls.Add(m_newButtons[i]);
}
If you try and do this more than once you may have to remove the old buttons from the control before adding the new ones.
buttons = new Button[30];
for(int i = 0; i < buttons.Length; i++) {
buttons[i] = new Button();
groupBox1.Controls.Add(buttons[i]);
}
this code will work but button will be added one over other so set location also
buttons = new Button[30];
for(int i = 0; i < buttons.Length; i++)
{
buttons[i] = new Button();
Point p=new Point(xvalue,yvalue);
buttons[i].Location = p;
groupBox1.Controls.Add(buttons[i]);
}
one thing you want to remember increment the x or y position by which you want to display it
Try this one out, I have just learned it myself:
public partial class Form1 : Form
{
Button[] btn = new Button[12];// <--------<<<Button Array
public Form1()
{
InitializeComponent();
}
private void Form1_Load (object sender, EventArgs e)
{
for (int i = 0; i < 12; i++)
{
btn[i] = new Button ( );
this.flowLayoutPanel1.Controls.Add(btn[i]);
}
}
// double click on the flow layoutPannel initiates this code
private void flowLayoutPanel1_Paint(object sender, PaintEventArgs e)
{
}
}

Categories