C#动态添加按钮单击是抛出参数超出范围例外
基本上我试图创建一个附件窗口利用保留在列表中的所有内容,以方便以后使用。因此,每当表单加载时,它都会遍历列表中的所有内容,并为它们创建标签和按钮。在我点击我的按钮之前没有错误。如果我点击任何X按钮,我会在点击+ =行上得到一个超出范围的参数。有趣的是为什么被称为?点击不应该为自己添加另一个事件处理程序。同样有趣的是,单击这个indicie比总计数大一个,所以它甚至可以执行那条线在我旁边,因为它永远不会迭代它的最大值。任何帮助将不胜感激。C#动态添加按钮单击是抛出参数超出范围例外
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();
}
在我的测试,attachmentFiles有计数3和attachmentNames具有3.指望形式的负载,没有任何问题。但是,在按钮点击我得到一个异常,因为不知何故它试图添加另一个点击监听器到一个按钮与i = 3(又名第四元素)
问题不在于事件订阅,而在于事件处理程序的执行。
您遇到此问题是因为为事件处理函数创建了闭包,但i
值由for
循环修改。 i
的最后一个值将是1 + attachmentNames.Count
,这将是每次调用事件处理函数时使用的值。
欲了解更多详情,请点击这里查看问题和答案:Access to Modified Closure。
要解决此问题,您可以指定i
另一个变量:
var currentAttachmentIndex = i;
btn_RemoveAttachment.Click += new System.EventHandler((s, e3) => {
removeAttachment(s,
e3,
attachmentFiles[currentAttachmentIndex].ToString(),
attachmentNames[currentAttachmentIndex].ToString())
});
或者你可以使用你在btn_RemoveAttachment
控制的Tag
属性已经捕捉到的值。
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())
});
请记住,不过,如果要删除从List
项目,该指标将无效。然而,理解闭包如何工作,应该可以帮助你在问题出现时解决这个问题(看起来就像你在第一次拆除后关闭表格)。
这是非常有用和信息丰富的。感谢您抽出宝贵时间写出来并解释它。 – Kyle 2014-10-10 18:04:38
据推测,attachmentFiles [我]是什么导致出界异常,或许attachmentFiles比attachmentNames有更少的元素?
为什么不在该行上设置断点并检查是什么导致了越界异常?
是的,但是,一切似乎都在检查中。两个列表都有一个最大数量低于我,并且两个都一起增加。 – Kyle 2014-10-10 17:46:11
我在点击+ =行上得到一个参数越界异常。有趣的是为什么被称为?点击是不应该到另一个事件处理程序添加到自己
看起来异常没有在事件订阅抛出(+ =),但在同一行声明的lambda函数
它也很有趣,点击这个indicie比总数还要大,所以它甚至在我旁边执行那条线,因为它永远不会迭代它的最大值。任何帮助将不胜感激
当您将lambda分配给事件时,我的值是固定的。 attachmentFile上的索引在移除元素时发生变化,但lambda使用的索引不会。我们来看一个例子。
假设我们有4个attchements(指数:附件)的阵列)
[0:att0,1:ATT1,2:ATT2,3:ATT3]
和4执行该按钮的lambda
[RemoveAt移除(0),RemoveAt移除(1),RemoveAt移除(2),RemoveAt移除(3)]
我们点击第二个按钮,它正确地移除从阵列的第二附接,现在我们有:
[0:att0,1:ATT2,2:ATT3]
[RemoveAt移除(0),RemoveAt移除(1),RemoveAt移除(2),RemoveAt移除(3)]
现在我们点击第四按钮。它试图删除索引为3的附件,并且由于该索引不再存在而抛出越界异常(即使它存在,它也可能不会指向正确的附件!)
另一种方法是修改'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);
}
然后你就可以修改你的removeAttachment方法是一个事件处理程序,并使用发件人为巴顿和Button.Tag性能检测按钮和相关标签:
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;
}
我喜欢Ryan的回答,但喜欢这种方法来移除物品,并且在每个物品移除后不关闭表单。 – 2014-10-10 18:36:44
点击似乎分配一个值,这似乎是越界。什么值和最小值和最大值? – TaW 2014-10-10 17:39:05
如果您提供[MCVE](http://stackoverflow.com/help/mcve) – 2014-10-10 17:46:46
,您可能会得到更多答案我只是添加了一些更多信息,以帮助解决问题。 – Kyle 2014-10-10 17:48:05