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(又名第四元素)

+0

点击似乎分配一个值,这似乎是越界。什么值和最小值和最大值? – TaW 2014-10-10 17:39:05

+0

如果您提供[MCVE](http://stackoverflow.com/help/mcve) – 2014-10-10 17:46:46

+0

,您可能会得到更多答案我只是添加了一些更多信息,以帮助解决问题。 – Kyle 2014-10-10 17:48:05

问题不在于事件订阅,而在于事件处理程序的执行。

您遇到此问题是因为为事件处理函数创建了闭包,但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项目,该指标将无效。然而,理解闭包如何工作,应该可以帮助你在问题出现时解决这个问题(看起来就像你在第一次拆除后关闭表格)。

+0

这是非常有用和信息丰富的。感谢您抽出宝贵时间写出来并解释它。 – Kyle 2014-10-10 18:04:38

据推测,attachmentFiles [我]是什么导致出界异常,或许attachmentFiles比attachmentNames有更少的元素?

为什么不在该行上设置断点并检查是什么导致了越界异常?

+0

是的,但是,一切似乎都在检查中。两个列表都有一个最大数量低于我,并且两个都一起增加。 – 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; 
} 
+0

我喜欢Ryan的回答,但喜欢这种方法来移除物品,并且在每个物品移除后不关闭表单。 – 2014-10-10 18:36:44