使列表中的按钮将唯一数据发送到方法

Make buttons in List send unique data to method

我制作了一个应用程序,它将按钮放置在一个网格中,用户可以在其中指定游戏场的大小。 我在列表中创建按钮,指定一些数据,如背景图像、大小和位置。然后我需要以某种方式让不同的按钮执行不同的代码。我想我可以用一种方法来做到这一点(如果没有任何好的方法来以编程方式创建方法),如果我能以某种方式让按钮向方法发送一条独特的信息来识别按下了哪个按钮。

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

    }

(如果你想知道我在做一个叫"Light's out"的游戏) 提前致谢!

Any_Button_Click 方法接收一个 object sender 被点击的按钮。您只需要将其转换为 Button:

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

您可以使用按钮的 Location 属性 来确定它在游戏板上的位置,或者您可以使用您在初始化时选择的任何信息将任意对象分配给按钮Tag 属性 像这样:

button.Tag = "someHelpfulString";

或者像这样:

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

(其中 xposypos 是按钮网格中的位置)

或者像这样:

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

(此处由您定义 ButtonInfoObject class。)

使用发送者对象获取按下的按钮名称:

void Any_Button_Click(object sender, EventArgs e)
{
     switch ((sender as Button).Name)
     {
         case "btn0":
             //...
             break;
         case "btn1":
             //...
             break;
         //...
     }
}

作为其他答案的替代方案,特别是为了在某种程度上解决 "good ways to programmatically create methods" 部分,C# 语言的一部分称为 Lambda Expressions。长话短说,您可以按照以下内容写一些东西:

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

这种方法的唯一缺点是,如果您打算稍后取消注册处理程序,则需要格外小心(参见 this question or this question 以供参考)。

编辑

正如评论中指出的那样,我忽略了 i 在整个 for 循环范围内的事实,因此在 lambda 中使用它几乎没有意义(所有处理程序都将使用它的最终值) .为了让它表现得像预期的那样,可以简单地在循环内定义一个变量,这样它就会在每次迭代结束时超出范围,并为每个处理程序单独存储:

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
}