• 动态控件、控件的生存周期和ViewState的运行细节


          这两天看了思归的动态控件状态问题相关文章,通过分析系统类库源码,对控件的生存周期和ViewState的运行细节有了更深一层的认识。
          思归文章里主要讨论的问题:下面两页差别很小,就是一句语句的前后次序有所不同,但PostBack后显示效果有所不同,请解释为什么显示效果不同。

    TestDyn1.aspx:

    <html>
    <body>
     <form id="form1" runat="server">
      <asp:Button id="btn" runat="server" Text="Click Me" OnClick="Button_Click" /> <br/>
      静态: <asp:DropDownList id="ddlStatic" runat="server">
      <asp:ListItem Text="1" Value="1" />
      <asp:ListItem Text="2" Value="2" />
      <asp:ListItem Text="3" Value="3" />
           </asp:DropDownList> <br/>
      动态:
     </form>
    </body>
    </html>
    <script language="C#" runat="server">
    void Page_Load(Object sender, EventArgs e)
    {
       DropDownList ddlDynamic = new DropDownList();
       ddlDynamic.ID = "ddlDynamic";

       form1.Controls.Add(ddlDynamic);

       if (!IsPostBack)
       {
     for (int i=1; i <=3; i++)
      ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));
       }

      
     
       if (IsPostBack)
       {
        Response.Write("[Page_Load]静态:" + ddlStatic.SelectedIndex + "<BR>");
     Response.Write("[Page_Load]动态:" + ddlDynamic.SelectedIndex + "<BR>");
       }
    }

    void Button_Click(Object sender, EventArgs e)
    {
     DropDownList ddlDynamic = (DropDownList)form1.FindControl("ddlDynamic");
          Response.Write("[Button_Click]静态:" + ddlStatic.SelectedIndex + "<BR>");
     Response.Write("[Button_Click]动态:" + ddlDynamic.SelectedIndex + "<BR>");
    }
    </script>

    TestDyn2.aspx:

    <html>
    <body>
     <form id="form1" runat="server">
      <asp:Button id="btn" runat="server" Text="Click Me" OnClick="Button_Click" /> <br/>
      静态: <asp:DropDownList id="ddlStatic" runat="server">
      <asp:ListItem Text="1" Value="1" />
      <asp:ListItem Text="2" Value="2" />
      <asp:ListItem Text="3" Value="3" />
           </asp:DropDownList> <br/>
      动态:
     </form>
    </body>
    </html>
    <script language="C#" runat="server">
    void Page_Load(Object sender, EventArgs e)
    {
       DropDownList ddlDynamic = new DropDownList();
       ddlDynamic.ID = "ddlDynamic";

       if (!IsPostBack)
       {
     for (int i=1; i <=3; i++)
      ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));
       }


       form1.Controls.Add(ddlDynamic);

     
       if (IsPostBack)
       {
        Response.Write("[Page_Load]静态:" + ddlStatic.SelectedIndex + "<BR>");
     Response.Write("[Page_Load]动态:" + ddlDynamic.SelectedIndex + "<BR>");
       }
    }

    void Button_Click(Object sender, EventArgs e)
    {
     DropDownList ddlDynamic = (DropDownList)form1.FindControl("ddlDynamic");
          Response.Write("[Button_Click]静态:" + ddlStatic.SelectedIndex + "<BR>");
     Response.Write("[Button_Click]动态:" + ddlDynamic.SelectedIndex + "<BR>");
    }
    </script>

          很多网友对问题进行了讨论,最后思归在动态控件状态问题(续)中给出了正确答案,大体意思是:动态控件是在Page_Load中新建的对象,处于原始状态,当我们在Page_Load里调用form1.Controls.Add()时,父控件form1处于Load阶段,它就会调用下拉框的一些方法让它经过Init->Load状态,其中的一个结果是在Init后面调用了TrackViewState,DropDownList的父类ListControl,override了TrackViewState,在其中调用了Items(ListItemCollection类)对象的TrackViewState。其结果是,如果你在form1.Controls.Add()之后改变动态DropDownList控件的Items的话,那些ListItem就会被保存下来,因为ListItemCollection对象 override 了 SaveViewState() 。而在form1.Controls.Add()之前添加的ListItem则不会被保存下来。
          根据思归的解释,大体明白了问题的原因,不过对于中间流程依然很模糊,不明白TrackViewState是干嘛的,也不明白Begin Tracking View State阶段都做了什么?为什么form1.Controls.Add()之前添加的ListItem不会被保存下来?上网查了一下TrackViewState在MSDN上的解释是:
          Control.TrackViewState :导致跟踪服务器控件的视图状态的更改,以便这些更改可以存储到服务器控件的 StateBag 对象中;
          IStateManager.TrackViewState:当由类实现时,指示服务器控件跟踪其视图状态更改;
          Calendar.TrackViewState :标记开始跟踪的起始点,并将对控件所做的更改作为控件视图状态的一部分进行保存。
    查看一下具体源码,基本上TrackViewState是将控件的marked字段置为true;
          现在来看思归提出的问题,在TestDyn1.aspx中,当执行form1.Controls.Add()时,会经历如下过程,调用到ListItemCollection.TrackViewState,将置ListItemCollection.marked为true。:

    Void System.Web.UI.ControlCollection.Add(Class System.Web.UI.Control)
    Void System.Web.UI.Control.AddedControl(Class System.Web.UI.Control,I4)
    Void System.Web.UI.Control.InitRecursive(Class System.Web.UI.Control)
    Void System.Web.UI.WebControls.ListControl.TrackViewState()
    Void System.Web.UI.WebControls.ListItemCollection.TrackViewState()

    然后程序执行ddlDynamic.Items.Add,我们看系统源码:

    public void Add(ListItem item)
    {
        
    this.listItems.Add(item);
        
    if (this.marked)
        
    {
            item.Dirty 
    = true;
        }

    }

    下面是Item的属性Dirty的定义:

    internal bool Dirty
    {
        
    get
        
    {
            
    if (!this.misc.Get(2))
            
    {
                
    return this.misc.Get(3);
            }

            
    return true;
        }

        
    set
        
    {
            
    this.misc.Set(2, value);
            
    this.misc.Set(3, value);
        }

    }

    通过item.Dirty = true这句,将导致Item的BItArray类型的私有字段misc中key值为2,3的value为true。
    当页面执行到SaveViewState阶段,在这一阶段中将会获取所有控件以及页面本身的 ViewState 集合的内容,所得到的视图状态随后序列化、进行哈希运算、进行 Base64 编码并关联到 __VIEWSTATE 隐藏字段。System.Web.UI.WebControls.ListItem重写了SaveViewState方法,代码如下:

    internal object SaveViewState()
    {
        
    if (this.misc.Get(2&& this.misc.Get(3))
        
    {
            
    return new Pair(this.Text, this.Value);
        }

        
    if (this.misc.Get(2))
        
    {
            
    return this.Text;
        }

        
    if (this.misc.Get(3))
        
    {
            
    return new Pair(nullthis.Value);
        }

        
    return null;
    }

    在这里展现出了misc字段的作用,也是整个问题的关键,如果不是之前执行form1.Controls.Add()触发了ListItemCollection.TrackViewState将misc.Get(2)和misc.Get(3)置为true的话,这里将返回null值,亦即ddlDynamic的所有Items的视图状态没有保存。到这里TestDyn2.aspx输出的原因已经一目了然了。

  • 相关阅读:
    Scrapy框架
    爬虫高性能相关
    存储库之MongoDB
    存储库之redis
    beautifulsoup
    pyecharts
    wxpy模块
    Gin框架
    Python的rabbitMQ
    Vue基础
  • 原文地址:https://www.cnblogs.com/end/p/668054.html
Copyright © 2020-2023  润新知