我有一个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}
您能否帮我解决一下如何为此次活动编写正确的代码。
答案 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";
}
它被称为访问修改后的闭包,并且与闭包的范围内容有关。
此消息表明您正在使用的变量 枚举可能会改变,而不是你所期望的 在它将被使用的时候。这不会抛出编译器 错误。
在内部使用这样的变量时的推荐方法 枚举(对于每个循环)是创建本地副本并使用它。
因此,我在i
中创建了变量index
的本地副本并使用了它。