通过编码创建事件

本文关键字:事件 创建 编码 | 更新日期: 2023-09-27 17:56:32

我有一个勾选列表框和一个面板。它们都是由代码创建的。现在我想创建一个事件,通过选中和取消选中选中的ListBox项的某个项目,可以启用或禁用此面板。我有以下代码,但它不起作用并在运行时引发异常。

CheckedListBox chlb = new CheckedListBox();
for (int i = 0; i < dr.Count(); i++)
{
    chlb.Items.Add( dr[i]["Value_Name"].ToString());
    if ((bool)dr[i]["HasText"] == true)
    {
        Panel pnltxt = new Panel();
        pnltxt.Size = new Size(630, 30);
        chlb.SelectedIndexChanged += (s, argx) => pnltxt.Enabled =
           (chlb.GetItemCheckState(i).ToString().Trim() == "Unchecked" ? false : true);
    }
}

错误消息是:

附加信息:InvalidArgument=值 '7' 对 'index' 无效。 + CHLB.GetItemCheckState(i) 'chlb.GetItemCheckState(i)' 抛出了类型为 'System.ArgumentOutOfRangeException' System.Windows.Forms.CheckState {System.ArgumentOutOfRangeException} 的异常

你能帮我如何为此事件编写正确的代码吗?

通过编码创建事件

您的问题在于 lambda 表达式和闭包。

当您创建 lambda 表达式(就像使用事件处理程序一样)并且它引用外部变量时,编译器需要创建一个闭包以包含该变量,以便 lambda 函数可以使用它。所以在这里:

for (int i=0; ......)
{
     //...
     chlb.SelectedIndexChanged += (s, argx) => pnltxt.Enabled =
         (chlb.GetItemCheckState(i).ToString().Trim() == "Unchecked" ? false : true);
     //..........................^
}

lambda 表达式使用 i 它是循环变量,它必须有一个包含它的闭包,以便在执行函数时可以使用它。但问题是,在将 lambda 表达式分配给事件处理程序时,它没有获得变量的副本,它实际上引用了与您循环的变量相同的变量。那么问题出在哪里呢?当您执行 lambda 表达式(即事件发生)时,i 的值是它在循环结束时的值!这是,dr.Count()(大概是7)。

解决方案是复制循环中的变量(如@astander的答案),以便它将关闭变量的副本,该副本将保留附加事件处理程序时的值。

所以:

for (int i=0; ......)
{
     int copyOfi = i;    // A new variable will get created every iteration!
     //...
     chlb.SelectedIndexChanged += (s, argx) => pnltxt.Enabled =
         (chlb.GetItemCheckState(copyOfi).ToString().Trim() == "Unchecked" ? false : true);
     //..........................^
     // And this will close to include only the `copyOfi` we created in this iteration
}

将代码更改为

if ((bool)dr[i]["HasText"]) {
    Panel pnltxt = new Panel();
    pnltxt.Size = new Size(630, 30);
    int index = i;
    chlb.SelectedIndexChanged += (s, argx) => pnltxt.Enabled =
        chlb.GetItemCheckState(index).ToString().Trim() != "Unchecked";
}

它被称为访问修改闭包,并且与闭包的范围规则有关。

看看访问修改后的闭合

此消息表明您正在使用的变量来自 枚举可能会更改,而不是您所期望的 在使用它的时候。 这不会抛出编译器 错误。

在 枚举(针对每个循环)是创建一个本地副本并使用它。

因此,我相应地在index中创建了变量i的本地副本并使用它。