1

I made a application which will place out buttons in a grid where the user specifies how big the playfield should be. I create the buttons in a list, specify some data like backgroundimage, size, and location. I then need to, in some way make the different buttons execute different code. I figured I could do this in one method, (if there aren't any good ways to programmatically create methods), if I could somehow make the buttons send a unique piece of information to the method to identify which button is pressed.

    public void buttonplacer()
    {
        int nbrofbtns = Form2.puzzlesize * Form2.puzzlesize;



        List<Button> btnslist = new List<Button>();
        for (int i = 0; i < nbrofbtns; i++)
        {

            Button newButton = new Button();
            btnslist.Add(newButton);
            this.Controls.Add(newButton);

            newButton.Name = "btn" + i.ToString();
            newButton.Width = btnsidelength;
            newButton.Height = btnsidelength;
            newButton.Top = btnsidelength * Convert.ToInt32(Math.Floor(Convert.ToDouble(i / Form2.puzzlesize)));
            newButton.Left = btnsidelength * Convert.ToInt32(Math.Floor(Convert.ToDouble(i)) - Math.Floor((Convert.ToDouble(i)) / (Form2.puzzlesize)) * (Form2.puzzlesize));
            newButton.BackgroundImage = Lights_out_.Properties.Resources.LightsOutBlack;




            newButton.Click += new EventHandler(Any_Button_Click);
        }
    }
    void Any_Button_Click(object sender, EventArgs e)
    {

    }

(If you want to know I'm doing a game called "Light's out") Thanks in advance!

adv12
  • 8,443
  • 2
  • 24
  • 48
Tommy R
  • 75
  • 5
  • Since your buttons have unique names, perhaps you can check sender's name to identify what button it is? Or its coordinates? – Yulia V Jan 13 '15 at 21:03
  • Place your Buttons in a dynamically setup a TableLayoutPanel with the correct number of rows/cols. In the Button handler, cast `sender` back to Button and determine its grid position using [TableLayoutPanel.GetPositionFromControl](http://msdn.microsoft.com/en-us/library/system.windows.forms.tablelayoutpanel.getpositionfromcontrol(v=vs.110).aspx). You could also get references to other Buttons at specific locations in the grid using [TableLayoutPanel.GetControlFromPosition](http://msdn.microsoft.com/en-us/library/system.windows.forms.tablelayoutpanel.getcontrolfromposition(v=vs.110).aspx). – Idle_Mind Jan 13 '15 at 23:06

3 Answers3

3

The Any_Button_Click method receives an object sender that is the button that got clicked. You just need to cast it to a Button:

void Any_Button_Click(object sender, EventArgs e)
{
    Button b = (Button)sender;
    // do stuff here
}

You can use the button's Location property to figure out where it sits on the game board, or you can assign an arbitrary object to the button with any information you choose at initialization time using the Tag property like this:

button.Tag = "someHelpfulString";

or like this:

button.Tag = new Tuple<int, int>(xpos, ypos);

(where xpos and ypos are positions in the button grid)

or like this:

button.Tag = new ButtonInfoObject(foo, bar, baz);

(Here it's up to you to define the ButtonInfoObject class.)

adv12
  • 8,443
  • 2
  • 24
  • 48
  • Alright, you mean that b, is technicaly the button, or at least it has all its information? So I could do as in the first example and retrieve the tag by b.Tag.ToString()? – Tommy R Jan 14 '15 at 05:56
  • How do I then set the buttonproberies of the actual button? In this case I want to chage the backgroundimage of the button to either black or white depending on what colour was there in the first place. I can check what the backgroundimage is by the variable but how do I change the button?? (sry for the double-comment) – Tommy R Jan 14 '15 at 06:48
  • Yes, `b` *is* the button, and you can retrieve the tag by `b.Tag.ToString()`. To change the background image, just say `b.BackgroundImage = < yourImage>`. – adv12 Jan 14 '15 at 13:40
1

As an alternative to other answers, and in particular to somewhat address the part "good ways to programmatically create methods", there is part of the C# language called Lambda Expressions. To keep long story short, you could write something along these lines:

newButton.Click += (s, e) =>
{
    //here you have access to all variables accessible in current scope,
    //including "newButton" and "i";
    //you could, for example, call some method passing "i" as an argument
    //or just put that method's code inside this block
};

The only downside of this approach is that you need to take some extra care if you're planning to unregister the handler at some later point (see this question or this question for reference).

EDIT

As pointed in comments I overlooked the fact that i stays in scope for the whole for loop, so using it inside lambda is pretty much pointless (all handlers will use it's final value). To make it behave like expected one can simply define a variable inside the loop so it goes out of scope at the end of each iteration and is stored separately for each handler:

var btnNo = i;
newButton.Click += (s, e) =>
{
    //use "btnNo" instead of "i"
    //you can still safely use "newButton" reference
    //since it's defined inside the loop
}
Community
  • 1
  • 1
Grx70
  • 10,041
  • 1
  • 40
  • 55
  • ...but wouldn't `i` change between this event registration and the firing of the event, leaving it with its final value for all buttons? (I'm sort of a newbie about closures, so sorry if I have this wrong.) – adv12 Jan 13 '15 at 21:33
  • Fortunately it wouldn't. See the "Variable Scope in Lambda Expressions" section of the mentioned MSDN page. Excerpt: "Variables (...) are stored for use in the lambda expression...". – Grx70 Jan 13 '15 at 21:41
  • 1
    I just did a quick test of this, and my assumption was correct. I added 10 buttons, each with a lambda Click handler that showed a MessageBox with the value of i, and it was 10 regardless of what button I clicked. – adv12 Jan 13 '15 at 21:50
  • @adv12 Thanks for pointing it out. I usually use `foreach` loop and define all necessary variables inside it so it missed my attention. – Grx70 Jan 14 '15 at 07:14
-1

Use the sender object to get the button's name that was pressed:

void Any_Button_Click(object sender, EventArgs e)
{
     switch ((sender as Button).Name)
     {
         case "btn0":
             //...
             break;
         case "btn1":
             //...
             break;
         //...
     }
}
Jonesopolis
  • 25,034
  • 12
  • 68
  • 112