C#动态添加的按钮单击将引发参数越界异常
基本上,我正在尝试创建一个附件窗口,利用列表中的所有内容方便以后使用。因此,每次加载表单时,它都会遍历列表中的所有内容,并为它们创建标签和按钮。在我单击我的按钮之前没有错误。如果我单击任何X按钮,我会在click+=行上得到一个参数越界异常。有趣的是它为什么被称为?单击不应向自身添加另一个事件处理程序。同样有趣的是,点击时的标记比总计数大一个,所以考虑到它的迭代次数永远不应该高于其最大计数,它如何执行该行就在我旁边。任何帮助都将不胜感激C#动态添加的按钮单击将引发参数越界异常,c#,event-handling,C#,Event Handling,基本上,我正在尝试创建一个附件窗口,利用列表中的所有内容方便以后使用。因此,每次加载表单时,它都会遍历列表中的所有内容,并为它们创建标签和按钮。在我单击我的按钮之前没有错误。如果我单击任何X按钮,我会在click+=行上得到一个参数越界异常。有趣的是它为什么被称为?单击不应向自身添加另一个事件处理程序。同样有趣的是,点击时的标记比总计数大一个,所以考虑到它的迭代次数永远不应该高于其最大计数,它如何执行该行就在我旁边。任何帮助都将不胜感激 ArrayList attachmentFiles
ArrayList attachmentFiles;
ArrayList attachmentNames;
public Attachments(ArrayList attachments, ArrayList attachmentFileNames)
{
InitializeComponent();
attachmentFiles = attachments;
attachmentNames = attachmentFileNames;
}
private void Attachments_Load(object sender, EventArgs e)
{
ScrollBar vScrollBar1 = new VScrollBar();
ScrollBar hScrollBar1 = new HScrollBar();
vScrollBar1.Dock = DockStyle.Right;
hScrollBar1.Dock = DockStyle.Bottom;
vScrollBar1.Scroll += (sender2, e2) => { pnl_Attachments.VerticalScroll.Value = vScrollBar1.Value; };
hScrollBar1.Scroll += (sender3, e4) => { pnl_Attachments.HorizontalScroll.Value = hScrollBar1.Value; };
pnl_Attachments.Controls.Add(hScrollBar1);
pnl_Attachments.Controls.Add(vScrollBar1);
Label fileName;
for (int i = 0; i < attachmentNames.Count; i++)
{
fileName = new Label();
fileName.AutoSize = true;
fileName.Text = attachmentNames[i].ToString();
fileName.Top = (i + 1) * 22;
pnl_Attachments.Controls.Add(fileName);
Button btn_RemoveAttachment = new Button();
btn_RemoveAttachment.Text = "X";
btn_RemoveAttachment.Tag = i;
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => removeAttachment(s, e3, attachmentFiles[i].ToString(), attachmentNames[i].ToString()));
btn_RemoveAttachment.Top = (i + 1) * 22;
btn_RemoveAttachment.Left = fileName.Right + 22;
pnl_Attachments.Controls.Add(btn_RemoveAttachment);
}
}
private void removeAttachment(object sender, EventArgs e, string file, string fileName)
{
attachmentNames.Remove(fileName);
attachmentFiles.Remove(file);
pnl_Attachments.Controls.Clear();
this.Close();
}
ArrayList附件文件;
ArrayList附件名称;
公共附件(ArrayList附件、ArrayList AttachmentFileName)
{
初始化组件();
attachmentFiles=附件;
attachmentNames=AttachmentFileName;
}
私有无效附件加载(对象发送方、事件参数)
{
滚动条vScrollBar1=新的VScrollBar();
滚动条hScrollBar1=新的HScrollBar();
vScrollBar1.Dock=DockStyle.Right;
hScrollBar1.Dock=DockStyle.Bottom;
vScrollBar1.Scroll+=(sender2,e2)=>{pnl_Attachments.VerticalScroll.Value=vScrollBar1.Value;};
hScrollBar1.Scroll+=(sender3,e4)=>{pnl_Attachments.horizontalcoll.Value=hScrollBar1.Value;};
pnl_Attachments.Controls.Add(hScrollBar1);
pnl_Attachments.Controls.Add(vScrollBar1);
标签文件名;
for(int i=0;iRemoveAttachment(s,e3,attachmentFiles[i].ToString(),attachmentNames[i].ToString());
btn_RemoveAttachment.Top=(i+1)*22;
btn_RemoveAttachment.Left=fileName.Right+22;
pnl_Attachments.Controls.Add(btn_RemoveAttachment);
}
}
私有void removeAttachment(对象发送方、事件参数、字符串文件、字符串文件名)
{
attachmentNames.Remove(文件名);
附件文件。删除(文件);
pnl_Attachments.Controls.Clear();
这个。关闭();
}
在我的测试中,attachmentFiles的计数为3,attachmentNames的计数为3。在表单加载时,不存在任何问题。但是,在点击按钮时,我得到了一个异常,因为它试图向一个I=3(又称第四个元素)的按钮添加另一个点击侦听器。大概是attachmentFiles[I]导致了越界异常,也许attachmentFiles的元素比attachmentNames少 为什么不在该行上设置一个断点并检查导致越界异常的原因 我在click+=行上得到一个参数越界异常。有趣的是它为什么被称为?单击不应向自身添加另一个事件处理程序 看起来异常不是在事件订阅(+=)上引发的,而是在同一行中声明的lambda函数上引发的 同样有趣的是,点击时的标记比总计数大一个,所以考虑到它的迭代次数永远不应该高于其最大计数,它如何执行该行就在我旁边。任何帮助都将不胜感激 将lambda指定给事件时,i的值是固定的。删除元素时,attachmentFiles处的索引会发生变化,但lambda用于访问它的索引不会发生变化。让我们来举个例子 假设我们有一个包含4个attchements的数组(索引:附件)) [0:att0,1:att1,2:att2,3:att3] 和4个执行此命令的按钮 [removeAt(0)、removeAt(1)、removeAt(2)、removeAt(3)] 单击第二个按钮,它将正确地从阵列中删除第二个附件,现在我们有: [0:att0,1:att2,2:att3] [removeAt(0)、removeAt(1)、removeAt(2)、removeAt(3)]
现在我们点击第四个按钮。它尝试删除索引为3的附件,并引发越界异常,因为该索引不再存在(即使它存在,也可能不会指向正确的附件!)问题不在于事件订阅,而在于事件处理程序的执行 遇到此问题是因为为事件处理程序创建了闭包,但值
i
被for
循环修改。i
的最后一个值将是1+attachmentNames.Count
,这将是事件处理程序每次调用所使用的值
有关发生这种情况的原因的更多详细信息,请查看此处的问题和答案:
要解决此问题,可以将i
分配给另一个变量:
var currentAttachmentIndex = i;
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => {
removeAttachment(s,
e3,
attachmentFiles[currentAttachmentIndex].ToString(),
attachmentNames[currentAttachmentIndex].ToString())
});
或者,您可以使用已在btn\u RemoveAttachment
控件的标记
属性中捕获的值
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => {
var senderButton = (Button)s;
var currentAttachmentIndex = (int)senderButton.Tag;
removeAttachment(s,
e3,
attachmentFiles[currentAttachmentIndex].ToString(),
attachmentNames[currentAttachmentIndex].ToString())
});
但是请记住,如果要从
列表中删除项目,索引将无效。但是,了解闭包是如何工作的,应该可以帮助您解决出现的问题(看起来您在第一次删除表单之后仍然关闭了表单)。另一种方法是修改“removeAttachment”方法,并将其用作所有按钮的事件处理程序
这方面的一个例子是:
for (int i = 0; i < attachmentNames.Count; i++)
{
var lbl_FileName = new Label
{
AutoSize = true,
Name = "Label" + i, // Give this a name so we can find it later
Text = attachmentNames[i],
Top = (i + 1) * 22
};
var btn_RemoveAttachment = new Button
{
Text = "X",
Tag = i,
Top = (i + 1) * 22,
Left = lbl_FileName.Right + 22
};
btn_RemoveAttachment.Click += removeAttachment;
pnl_Attachments.Controls.Add(lbl_FileName);
pnl_Attachments.Controls.Add(btn_RemoveAttachment);
}
单击似乎指定了一个值,这似乎超出了范围。值是多少?最小值和最大值是多少?如果您提供
private void removeAttachment(object sender, EventArgs e)
{
// Get associated Label and Button controls
var thisButton = sender as Button;
var index = Convert.ToInt32(thisButton.Tag);
var thisLabel = (Label) Controls.Find("NameLabel" + index, true).First();
// Remove the files
int itemIndex = attachmentNames.IndexOf(thisLabel.Text);
attachmentNames.RemoveAt(itemIndex);
attachmentFiles.RemoveAt(itemIndex);
// Disable controls and strikethrough the text
thisButton.Enabled = false;
thisLabel.Font = new Font(thisLabel.Font, FontStyle.Strikeout);
thisLabel.Enabled = false;
}