Asp.net:如何添加引用动态创建元素的事件处理程序

时间:2018-05-24 19:19:04

标签: c# asp.net

我有一个asp.net程序,可以创建一个简单的调查表供用户回答。 大多数问题都使用下拉列表,答案分数为1-5(不好),我试图将事件处理程序添加到下拉列表对象中,这样只有当用户选择两者之间的分数时才会启用评论框。 1和2。

然而,当我为事件处理程序添加委托lambda调用时,而不是每个下拉列表影响他们自己的相应注释框,它们似乎都只指向添加的最后一个(并且它们工作一次,然后不再工作,只有最后一个ddl继续有预期的行为。)

我的代码:

   //Called from Page_Load
   private void PopulateSurvey()
   {
        btnSubmit.Enabled = true;
        List<Question> questions = (from p in context.Questions
                                    join q in context.Survey_Questions on p.ID equals q.QuestionID
                                    where q.SurveyID == surveyid
                                    select p).ToList();
        Table tbl = new Table();
        tbl.Width = Unit.Percentage(100);
        TableRow tr;
        TableCell tc;
        TableCell tc1;
        TableCell tc2;
        TextBox txt;
        CheckBox cbk;
        DropDownList ddl = new DropDownList();

        foreach (Question q in questions)
        {

            if (q.Division.Equals("General") || q.Division.Equals(ddlDivisions.SelectedValue.ToString()))
            {
                tr = new TableRow();
                tc = new TableCell();
                tc.Width = Unit.Percentage(55);
                tc.Text = q.Text;
                tc.Attributes.Add("id", q.ID.ToString());
                tr.Cells.Add(tc);
                tc = new TableCell();

                if (q.QuestionType.ToLower() == "singlelinetextbox")
                {
                    txt = new TextBox();
                    txt.ID = "txt_" + q.ID;
                    //txt.Width = Unit.Percentage(40);
                    tc.Controls.Add(txt);
                }

                if (q.QuestionType.ToLower() == "multilinetextbox")
                {
                    txt = new TextBox();
                    txt.ID = "txt_" + q.ID;
                    txt.TextMode = TextBoxMode.MultiLine;
                    //txt.Width = Unit.Percentage(40);
                    tc.Controls.Add(txt);
                }

                if (q.QuestionType.ToLower() == "singleselect")
                {
                    ddl = new DropDownList();
                    ddl.ID = "ddl_" + q.ID;
                    //ddl.Width = Unit.Percentage(41);
                    if (!string.IsNullOrEmpty(q.Options))
                    {
                        string[] values = q.Options.Split(',');
                        foreach (string v in values)
                            ddl.Items.Add(v.Trim());
                    }
                    ddl.AutoPostBack = true;
                    tc.Controls.Add(ddl);
                }
                //tc.Width = Unit.Percentage(60);
                tr.Cells.Add(tc);

                //Add comment row
                tc1 = new TableCell();
                //tc.Width = Unit.Percentage(5);
                tc1.Text = "Comentario: ";
                tc1.Attributes.Add("id", q.ID.ToString());
                //tc1.Visible = false;
                tr.Cells.Add(tc1);

                tc2 = new TableCell();
                txt = new TextBox();
                txt.ID = "txt_" + q.ID + "comment";
                txt.TextMode = TextBoxMode.SingleLine;
                //txt.Width = Unit.Percentage(25);
                tc2.Controls.Add(txt);
                tr.Cells.Add(tc2);

                ddl.SelectedIndexChanged+= (sender, e) => ScoreChanged(sender, e, tc1,tc2, ddl.SelectedIndex);

                tbl.Rows.Add(tr);
            }
        }
        pnlSurvey.Controls.Add(tbl);
    }

    protected void ScoreChanged (object sender, EventArgs e, TableCell tc1, TableCell tc2, int score)
    {
        if( score <2)
        {
            tc1.Visible = false;
            tc2.Visible = false;
        }
        else
        {
            tc1.Visible = true;
            tc2.Visible = true;
        }
    }

首先想到的是,事件处理程序可能会保留引用本身,而不是每个问题创建的孤立对象,因此所有事件处理程序最终都使用相同的tc1和tc2引用,因此只有最后一个对象?是这样的,如果是这样,我该怎么回事呢?

1 个答案:

答案 0 :(得分:0)

你的问题在这一行:

ddl.SelectedIndexChanged +=
    (sender, e) => ScoreChanged(sender, e, tc1,tc2, ddl.SelectedIndex);

这里有两件事很重要:

  • 您正在使用匿名方法来调用ScoreChanged
  • 您正在将tc1tc2作为参数传递给ScoreChanged方法。您已在代码块的开头定义了这些变量,外部循环。

这个上下文中的神奇词是闭包。由于tc1tc2的定义超出了匿名方法的范围,因此它们会变成闭包。这意味着在定义方法时不会有值,但在调用时。由于您不断地在foreach循环中覆盖变量的值,因此在调用时,这些变量将具有循环的最后一次迭代的值。

解决方案很简单:在循环中声明变量 。这将为foreach的每次迭代创建一个新的闭包:

TableRow tr;
TableCell tc;
TextBox txt;
CheckBox cbk;

foreach (Question q in questions)
{
    TableCell tc1;
    TableCell tc2;
    DropDownList ddl; //Don't forget to include ddl, since you are using its selected index
    //...

更一般地说:不要在C#中的方法开头做这样的“声明”块。在第一次使用它时声明变量(除非有充分的理由不这样做,例如你希望它成为闭包的一部分)。有很多很好的理由,你只是经历过其中一个。另一个是当您将代码的一部分转换为具有Visual Studio重构功能的方法时,您将把预先声明的变量作为ref参数传递。这些是最明显的原因。还有更多。