程序的功能是:动态生成试题,然后提交后,核对答案首先我在Page_Load动态添加控件,然后增加一个button,button事件里用到之前动态添加的控件,但在实际运行时,PostBack之后,系统提示错误“System.NullReferenceException: 未将对象引用设置到对象的实例。”protected void Page_Load(object sender, EventArgs e)
    {        //字符串生成器
        SqlConnectionStringBuilder bldr = new SqlConnectionStringBuilder();
        ..........
        //生成SqlConnection对象
        SqlConnection cn = new SqlConnection(bldr.ConnectionString);
        cn.Open();        
        
        string strSQL = " SELECT TOP (10) subjectid, subjectcontent, rightkey FROM judge ORDER BY NEWID()";
                //创建SqlCommand对象             
        SqlCommand cmd_judge = new SqlCommand(strSQL, cn);
        SqlDataReader Qreader = cmd_judge.ExecuteReader();        int Qnumber = 1;
        //动态创建控件        while (Qreader.Read())
        {
            Literal QLiteral = new Literal();
            QLiteral.Mode = LiteralMode.Transform;
            Panel1.Controls.Add(QLiteral);
            if (!IsPostBack)
            {
                if (Qnumber != 1) QLiteral.Text = QLiteral.Text + "<br><br>";
                QLiteral.Text = QLiteral.Text + Qnumber.ToString() + "&nbsp" + "【" + Qreader["subjectid"].ToString() + "】" + Qreader["subjectcontent"].ToString();
            }
                        //加入正确答案的label
            Label Alabel = new Label();
            Panel1.Controls.Add(Alabel);
            if (!IsPostBack)
            {
                Alabel.ID = "Alab" + Qnumber.ToString();
                Alabel.Text = Qreader["rightkey"].ToString().Trim();
                Alabel.ForeColor = Color.Red;
                Alabel.Visible = false;
            }
                        //加入选项
            RadioButtonList Qradio = new RadioButtonList();
            Panel1.Controls.Add(Qradio);
            if (!IsPostBack)
            {
                Qradio.ID = "Qrad" + Qnumber.ToString();
                Qradio.Items.Add("正确");
                Qradio.Items[0].Value = "True";
                Qradio.Items.Add("错误");
                Qradio.Items[1].Value = "False";
            }
            
            
            Qnumber++;
                    }
   }
button部分
RadioButtonList tmpRad = (RadioButtonList)Panel1.FindControl("Qrad1" );
        Response.Write(tmpRad.Items[0].Text.ToString());

解决方案 »

  1.   

    问题估计是出在动态加载的控件上,IsPostBack后,出现的Null异常,估计是上前加载的控件没有重新加载上,所以为Null了,建议设置断点一步步调试,看看是那里出的错。
    如果IsPostBack后,保证持行了  while (Qreader.Read())
            {
                Literal QLiteral = new Literal();
                QLiteral.Mode = LiteralMode.Transform;
                Panel1.Controls.Add(QLiteral);
                if (!IsPostBack)
                {
                    if (Qnumber != 1) QLiteral.Text = QLiteral.Text + "<br><br>";
                    QLiteral.Text = QLiteral.Text + Qnumber.ToString() + "&nbsp" + "【" + Qreader["subjectid"].ToString() + "】" + Qreader["subjectcontent"].ToString();
                }
                            //加入正确答案的label
                Label Alabel = new Label();
                Panel1.Controls.Add(Alabel);
                if (!IsPostBack)
                {
                    Alabel.ID = "Alab" + Qnumber.ToString();
                    Alabel.Text = Qreader["rightkey"].ToString().Trim();
                    Alabel.ForeColor = Color.Red;
                    Alabel.Visible = false;
                }
                            //加入选项
                RadioButtonList Qradio = new RadioButtonList();
                Panel1.Controls.Add(Qradio);
                if (!IsPostBack)
                {
                    Qradio.ID = "Qrad" + Qnumber.ToString();
                    Qradio.Items.Add("正确");
                    Qradio.Items[0].Value = "True";
                    Qradio.Items.Add("错误");
                    Qradio.Items[1].Value = "False";
                }
                
                
                Qnumber++;
                        }代码的话,把Alabel.ID = "Alab" + Qnumber.ToString();
    语句提到if(!IsPostBack)外试试
      

  2.   

    IsPostBack之后,确实执行了while循环
    但是如果把Qradio.ID = "Qrad" + Qnumber.ToString();
                    Qradio.Items.Add("正确");
                    Qradio.Items[0].Value = "True";
                    Qradio.Items.Add("错误");
                    Qradio.Items[1].Value = "False";
    这些放到if(!IsPostBack)之外,问题是解决了
    但多了一遍题目与选项的输出问题是不是出在IsPostBack之后,那些动态创建的控件的 信息没有被保存下来?~
      

  3.   

    IsPostBack后,动态加载的控件必须被重新加载一次,关键是ID必须一样。
    如果是重新加载了一次的话,你可以考虑在加载前把Items清空:Qradio.Items.Clear()
      

  4.   

    Qradio不是动态加载的吧,在While外加一句Qradio.Items.Clear();
      

  5.   

    你说的这个过程我不是很懂~~~这个动态添加控件的控件,我有参考了一下思归的这个文章其中的TestDyn1.aspx按照他的思路,达到了我之前的一个目的
    就是,IsPostBack后,页面呈现的还是第一次生成的试卷但是就是button事件有问题,但我看他那个例子里的引用,好像差不多思路的啊...
      

  6.   

    突然看到
    好像ID的赋值改在if(!IsPostBack)外面我试试先
      

  7.   


    Panel1.Controls.Add(...); 
    if (!IsPostBack) 

      ...

    改成如下试一试:
    if (!IsPostBack) 

      ...

    Panel1.Controls.Add(...); 
      

  8.   

               Alabel.ID = "Alab" + Qnumber.ToString();
               Panel1.Controls.Add(Alabel);只有ID必须在将控件加入控件树之前必须赋值,其它一切属性都可以在加入之后再赋值。
      

  9.   

    另外,你维护Qnumber的逻辑是错误的。这也是许多代码错误的根源。通常,Qnumber应该在ViewState集合中保存。假设在一次回发中添加了控件,那么Qnumber就应该被保存在下来。下一次执行Page_Load时要重建相应数量的子控件。这与是否更新了数据库无关。而你依据数据库中的记录数量来决定建立子控件的数量,这个逻辑不对。
      

  10.   

    TO sp1234:
    谢谢指点,很精确的点出问题
    你说的那个问题我再好好看下
      

  11.   

    TO sp1234: 
    这个控件的数量
    我当时的思路是,随机的取出10个题,这个是确定的,然后就依次把它们显示出来
    你的意思是,用Qnumber确定控件的数量,然后添加
    而不是用while (Qreader.Read()){....}这个思路?
      

  12.   

    一般化地逻辑可能是:
    DataSet DataContext
    {
      get{ if(ViewState["dc"]==null)return0; else return (DataSet)ViewState["dc"];}
      set{ ViewState["dc"]=value; }
    }void CreateSubControls(bool flag)
    {
      Placeholder1.Controls.Clear();
      for(int i=0;i<DataContext.Rows.Count)
      {
         Literal QLiteral = new Literal();
         QLiteral.ID = "Literal" + QNum.ToString();
         Placeholder1.Controls.Add(QLiteral);
          Label Alabel = new Label();
             ......//根据DataContext[i]设置QLiteral的值
         Alabel.ID= .....;
         .....Add(Alabel);
         if(flag)
             ......//根据DataContext[i]设置Alabel的值
          RadioButtonList Qradio = new RadioButtonList();
         Qradio.ID = .....;
         .....Add(Qradio);
         if(flag)
             ......//根据DataContext[i]设置Qradio的值
         Qradio.SelectedIndexChanged += new EventHandler(abc);
      }
    }private bool RefreshFlag=false;void abc(object sender,EventArgs e)
    {
       ..... // 获得客户端点击的问题号 pos,并且更新 DataContext[pos] 中的数据。
       RefreshFlag = true;
    }void Page_load(....)
    {
        CreateSubControls(false);
    }void Page_PreRender(....)
    {
      if(!this.IsPostback)
      {
          DataContext= ..... //从数据库中读出记录并放入DataSet中。
          RefreshFlag = true;
       }
       if(RefreshFlag)
          CreateSubControls(true);
    }
    实际上,Page_Load中逻辑上根本不需要判断 !IsPostback。许多“asp.net入门”代码都是非常简单的,因此把这个判断相关的代码放在 Page_load中。正确的做法是放在 PreRender 中。
      

  13.   

    开头几行改两个地方:DataSet DataContext 

      get{ return (DataSet)ViewState["dc"];} 
      set{ ViewState["dc"]=value; } 
    } void CreateSubControls(bool flag) 

      Placeholder1.Controls.Clear(); 
      if(DataContext!=null)
        for(int i=0;i <DataContext.Rows.Count) 
        {
      

  14.   

    所有的动态控件在页面上(IsPostBack时)看上去往往需要“创建两次”!在Page_Load中需要绘制子控件,但是不能从数据库中读取值,因为需要的是asp.net控件自动保存的当前客户端操作状态。在SelectedIndexChanged 或者其它任意事件中,可能需要调整 DataContext 中的数据。例如有一个选项按钮是“我放弃回答这个问题”,当用户选择这个按钮时在事件中需要从DataContext中删除这个记录。或者按钮是“我希望再多回答几个相关问题”,那么你就需要在事件中在DataContext中插入几行。事件中,最新的客户端操作状态同步到DataContext中,但是并不立即重建子控件,因为可能有很多事件随后发生,不能每一个事件都重建控件,每一个事件只是做一个“RefreshFlag = true; ”标记而已。PreRender处理中根据RefreshFlag 重新绘制子控件,包括在 (!IsPostback) 时第一次绘制子控件。实际上只有当页面即将输出html的最后时刻才需要创建最终的输出控件树,当第一次访问页面时也是在 PreRender中绘制,而不是在Page_Load中绘制。
      

  15.   

    类似的逻辑可以参见我的另一个答复:http://topic.csdn.net/u/20080308/14/866f3d1a-478c-4b36-a954-488b9bfdd012.html?seed=1367203162
      

  16.   

    “在Page_Load中需要绘制子控件,但是不能从数据库中读取值”这是很重要的。用DataContext中的私有状态数据才是正确的。只有这样,随后的有关控件才能准确地发生在相应的控件上。在这个方法中的逻辑是:需要重建与上一次页面输出时完全相同的控件树,以便asp.net填回状态值并知道那个控件更新了(随后触发事件)。如果你用数据库值来重建子控件,数据库是可能被在页面之外的后台改变的,asp.net就乱了。
      

  17.   

    有两个asp.net编程的“坏味道”比较常见,特别是充斥在网上和一些“教材”中的简单范例中,这些范例完成的行为非常简单,以至于错误的逻辑也可以勉强通过。但是一旦添加一点动态行为就容易整体出错。所以我总结出两个清爽你的asp.net程序味道的要点:1. Page_Load中不应该出现 if(!IsPostback) 这样的代码。这个做法让你明了应该尽量把工作推迟到Render之前才做,从而你的各种逻辑不会在前边的事件中早早纠缠在一起。2. 客户端激活的事件不要更新任何其它的控件,而应该遵循MVC模式,来更新控制数据。只有在Render之前才需要根据控制数据来刷新显示。
      

  18.   

    來結貼了好好學習中ing感謝sp1234