通过编码创建事件

时间:2014-04-03 17:30:46

标签: c# .net

我有一个checkedListBox和一个面板。它们都是由代码创建的。现在我想创建一个事件,通过选中和取消选中checkedListBox项的项,此面板被启用或禁用。我有以下代码,但它不起作用,并在运行时抛出异常。

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}

您能否帮我解决一下如何为此次活动编写正确的代码。

2 个答案:

答案 0 :(得分:1)

你的问题在于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
}

答案 1 :(得分:0)

将您的代码更改为

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

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

查看Access to modified closure

  

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

     

在内部使用这样的变量时的推荐方法   枚举(对于每个循环)是创建本地副本并使用它。

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