创建MouseEventHandler的委托新实例

时间:2012-05-23 14:29:33

标签: c# delegates mouseevent anonymous-function

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

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

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

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

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

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

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

    }

这里是一个正在发生的事情的图像,只是为了清晰起见:

example of error

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

7 个答案:

答案 0 :(得分: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);
        };
}

答案 1 :(得分: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),并为每个单独的按钮调用generatePopup方法(它是tempIndex的本地版本)。关闭变量关闭发生在Lambda之外,所以你应该在指定Lambda之前设置一个局部变量,它将被正确捕获。

尝试一下,让我们知道。

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

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

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

答案 2 :(得分:0)

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

答案 3 :(得分: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
    {
        ...
    }
}

答案 4 :(得分: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;

参考thisEvents and Delegates simplified

要创建自定义控件,请按照以下步骤操作 - Creating a Custom Control in Winforms - Part 1Developing Custom Windows Forms Controls with the .NET Framework

public partial class UserControl1 : TextBox

答案 5 :(得分:0)

这应该有效:

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

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

答案 6 :(得分:0)

您必须在内部作用域中声明索引,否则它仍将引用您的循环参数。

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

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