3

动态创建按钮时,我希望有一个新的 MouseEventHandler。我是这样做的,

this.Controls[btnAdd.Name].MouseClick += new MouseEventHandler(generalMethods.generatePopup());

但是,由于我需要将参数传递给事件,我让 generatePopup 接受一个表示按钮编号的整数(所以我可以进一步使用)。在过去,我只会使用匿名委托来传递额外的参数,但这似乎不起作用,因为我想创建一个 NEW 实例,因此;

this.Controls[btnAdd.Name].MouseClick += delegate(object sender, MouseEventArgs e) { new MouseEventHandler( generalMethods.generatePopup(sender, e, i); };

似乎错误出在新的 MouseEventHandler 上。如果我把它拿出来,它工作正常,我可以传递额外的数据,但因为它不是一个新实例,它不会给每个按钮自己的事件,就像这样;

this.Controls[btnAdd.Name].MouseClick += new MouseEventHandler(generalMethods.generatePopup());

有谁知道如何解决这个问题?例如仍然使用匿名委托方法,但仍然创建事件的新实例?

编辑:嗯,我试过这个(我相信参数通过了)但似乎不是为每个按钮创建一个新的鼠标事件处理程序,而是对所有按钮使用相同的,因为我设置了一个标签来测试它获得正确的按钮编号,但弹出窗口上的所有标签都返回最后一个按钮编号而不是正确的编号。有任何想法吗?

编辑2:这是我生成按钮的代码块:

 for (int i = 0; i <= count && i < 2; i++)
        {

            Button btnAdd = new Button();
            btnAdd.Text = dataTable.Rows[i]["deviceDescription"].ToString();
            btnAdd.Location = new Point(x, y);
            btnAdd.Tag = i;
            btnAdd.Name = "btn" + i.ToString();
            btnAdd.BackColor = Color.Green;

            this.Controls.Add(btnAdd);

            this.Controls[btnAdd.Name].MouseClick += (sender, e) =>
{
    int index = i;
    generalMethods.generatePopup(sender, e, index);
};

这是我的 generatePopup 方法,即 mouseEventHandler:

public void generatePopup(object sender, MouseEventArgs e, int buttonNumber)
    {
      //  DBConnector mDBConnector = new DBConnector();
      //  DataTable dataTable = mDBConnector.Select("SELECT * FROM devices WHERE deviceID = " + buttonNumber);


        DeviceBreakdownPopup popupDevice = new DeviceBreakdownPopup();
        popupDevice.lblDeviceNo.Text = buttonNumber.ToString();

        PopupWindow popup = new PopupWindow(popupDevice);
        popup.Show(Cursor.Position);

    }

这是正在发生的事情的图像,只是为了清楚起见:

错误示例

在这里,我们看到两个弹出用户控件都给出了标签“2”,而我需要每个都有正确的值,“1”和“2”。

4

7 回答 7

2

尝试这个:

this.Controls[btnAdd.Name].MouseClick += new MouseEventHandler(
    (sender, evt) => {
        generalMethods.generatePopup()
    }
);

new MouseEventHandler(...)部分是可选的 - 编译器从赋值的左侧计算出参数类型+=

编辑您的代码的问题是它访问修改后的闭包。您应该从中创建一个临时变量i,并在您的 lambda 中使用它来解决问题。

for (int i = 0; i <= count && i < 2; i++)
    {

        Button btnAdd = new Button();
        btnAdd.Text = dataTable.Rows[i]["deviceDescription"].ToString();
        btnAdd.Location = new Point(x, y);
        btnAdd.Tag = i;
        btnAdd.Name = "btn" + i.ToString();
        btnAdd.BackColor = Color.Green;

        this.Controls.Add(btnAdd);
        var temp = i;
        this.Controls[btnAdd.Name].MouseClick += (sender, e) =>
            {
            int index = temp;
            generalMethods.generatePopup(sender, e, index);
        };
}
于 2012-05-23T14:35:16.063 回答
1

返回什么类型generatePopup?如果它MouseEventHandler已经返回一个,那么你应该没问题(只要为每个实例生成一个新的处理程序)。类似于以下内容:

btnAdd.MouseClick += generalMethods.generatePopup();

/* in generalMethods */
private static int ButtonIndex = 0;
public MouseEventHandler generatePopup()
{
   int tempIndex = ButtonIndex++;
   return new MouseEventHandler((sender, ea) => generalMethods.generatePopup(sender, ea, tempIndex));
}

您这样做的原因是因为该generatePopup方法将为每个按钮创建一个新MouseEventHandler按钮。生成的每个处理程序将为您创建的 lambda 创建一个闭包,添加tempIndex我们设置为本地字段的变量。因此,当创建处理程序时,会在 lambda(包括 tempIndex)上创建新的闭包,并将为每个单独的按钮(它是 tempIndex 的本地版本)调用 generatePopup 方法。变量的闭包发生在 Lambda 之外,因此您应该在指定 Lambda之前设置一个局部变量,它将被正确捕获。

试一试,让我们知道。

编辑:所以我看到您正在循环生成按钮。是否会出现动态生成它们的情况(即:不在该循环内,有时在初始创建之后)?如果是这样,您已经获得了按钮本身的所有信息:

(sender, ea) => generalMethods.generatePopup(sender, ea, (int)((Control)sender).Tag)

由于您已经将索引附加到标签,因此您不必担心为不同的按钮使用不同版本的索引。在您当前的版本中,您正在尝试获取循环变量本身的引用(它会被重用),因此当您完成循环时, MouseEventHandler 的所有实例都会引用循环变量的最终状态。除非您创建一个临时变量来存储中间值,否则循环变量和 Lambda 不会混合使用。

于 2012-05-23T14:58:33.493 回答
0

这应该有效:

this.Controls[btnAdd.Name].MouseClick += 
  (sender, e) => generalMethods.generatePopup(sender, e, i);

这将创建一个具有唯一参数的唯一匿名方法,由每个按钮单击事件调用。

于 2012-05-23T14:37:54.860 回答
0

我唯一能想到的是创建一个继承的控件并添加您自己的事件和属性。您的事件将在实例化时填充属性,并在触发时调用 mouseclick。

于 2012-05-23T14:36:09.900 回答
0

使用标准事件模式。您可以检索事件的源并将其转换为所需的类型。

this.Controls[btnAdd.Name].MouseClick += new MouseEventHandler(MyControl_MouseClick);

//...

void MyControl_MouseClick(object sender, MouseEventArgs e)
{
    var button = sender as Button;

    if (button.Name == "Add") // here is how to retrieve the button name
    {
        ...
    }
}
于 2012-05-23T14:36:24.127 回答
0

你不能这样做。

委托语法后面是附加该特定事件的方法。您可以为该控件创建自定义控件和自定义事件。

这里我们如何创建事件:

  // Define a delegate named LogHandler, which will encapsulate
    // any method that takes a string as the parameter and returns no value
       public delegate void LogHandler(string message);

    // Define an Event based on the above Delegate
       public event LogHandler Log;

参考这个简化的事件和代表

要创建自定义控件,请遵循以下 -在 Winforms 中创建自定义控件 - 第 1 部分使用 .NET Framework 开发自定义 Windows 窗体控件

public partial class UserControl1 : TextBox
于 2012-05-23T14:36:28.867 回答
0

您必须在内部范围内声明索引,否则它仍会引用您的循环参数。

for (int i = 0; i < 42; i++)
{
    this.Controls[name].MouseClick += (sender, e) =>
    {
        int index = i;
        generalMethods.generatePopup(sender, e, index);
    }
}

您不需要为每个按钮指定一个处理程序(所有人都可以正常工作),但您必须为每个按钮授予其自己的索引对象。

于 2012-05-23T14:47:25.840 回答