思归呓语

衣带渐宽终不悔,为伊消得人憔悴
随笔 - 413, 评论 - 2971, 引用 - 245

导航

关于

标签

每月存档

最新留言

广告

 

在论坛上,动态控件好象是永久的话题。大家都知道要动态控件起作用,PostBack时需要重新生成或装载(LoadControl),而且需要深入了解其状态的变化过程。

有个同事另谋高就,要离开我们工作的地方了。我给她出了一道出门考题,同时也叫其他手下一起参加。这题目是这样的:

下面两页差别很小,就是一句语句的前后次序有所不同,但PostBack后显示效果有所不同,请解释为什么显示效果不同,并且解释正确显示的那页(你知道是哪页,对么?)中Response.Write的输出结果

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>

 

了解ViewState与控件生命周期的人,对这题自然不在话下。鉴于Lostinet对此有深入研究,本题禁止他参与,

注:本题是从我在CSDN上回答过的一个问题改编而来的

打印 | 张贴于 2004-10-20 07:06:00 | Tag:暂无标签

留言反馈

#回复: 动态控件的状态问题 编辑
改成这样也没用

public void InstantiateIn(Control container)
{
TextBox textBox = new TextBox();
container.Controls.Add(textBox);
textBox.ReadOnly = true;
textBox.BackColor = Color.Transparent;
textBox.BorderWidth = new Unit(0);
textBox.Width = new Unit(this.width);
textBox.ID = "txt" + bindField;
textBox.DataBinding += new EventHandler(OnDataBinding);


}
2007-12-05 11:04:00 | [匿名:ylg]
#回复: 动态控件的状态问题 编辑
我为GridView动态定义了一个模板,在 InstantiateIn() 方法里动态创建了一个TextBox文本框,请问在页面POSTBACK的时候,如何何持TEXTBOX及它的状态?谢谢.目前是一回传TEXTBOX就不见了.
public class TextBoxItemTemplate : ITemplate, INamingContainer
{
private string bindField;
private string width = "100";
public TextBoxItemTemplate(string bindField, string width)
{
this.bindField = bindField;
this.width = width;
}

public string Width
{
get { return width; }
}

public void InstantiateIn(Control container)
{
TextBox textBox = new TextBox();
textBox.ReadOnly = true;
textBox.BackColor = Color.Transparent;
textBox.BorderWidth = new Unit(0);
textBox.Width = new Unit(this.width);
textBox.ID = "txt" + bindField;
textBox.DataBinding += new EventHandler(OnDataBinding);
container.Controls.Add(textBox);

}

protected void OnDataBinding(object sender, EventArgs e)
{
TextBox textBox = (TextBox)sender;
GridViewRow container = (GridViewRow)textBox.NamingContainer;
string v = DataBinder.Eval(container.DataItem, bindField) + "";
textBox.Text = v;
}

}
2007-12-05 11:03:00 | [匿名:ylg]
#回复: 动态控件的状态问题 编辑
本质上是因为ListItem并非dropdownlist之子控件,因此不会发生页周期补偿。当dropdownlist被加入form中时,dropdownlist补偿了loadviewstate过程,然而如果此时list item并不存在,则list item之状态将被丢失。(但如果list item是子控件,则不会发生状态丢失)
这就是asp.net框架之AddedControl方法,和ControlsViewState字段的用意所在。
2007-03-23 10:57:00 | [匿名:ych]
#动态控件、控件的生存周期和ViewState的运行细节 编辑
这两天看了思归的动态控件状态问题相关文章,通过分析系统类库源码,对控件的生存周期和ViewState的运行细节有了更深一层的认识。
2007-03-08 14:39:00 | [匿名:风生水起]
#re: 动态控件的状态问题 编辑
拜託妳齣的題目有問題ing
2006-08-28 22:06:00 | [匿名:香水懷懷]
#re: 动态控件的状态问题 编辑
"鉴于Lostinet对此有深入研究,本题禁止他参与"
这样说就不对了吧。讨论之后更需要有人能透彻的分析一下,比如Lostinet。毕竟大家都想知道为什么,不是吗?
2006-07-11 10:18:00 | [匿名:net]
#动态控件的状态问题 编辑


在论坛上,动态控件好象是永久的话题。大家都知道要动态控件起作用,PostBack时需要重新生成或装载(LoadControl),而且需要深入了解其状态的变化过程。
有个同事另谋高就,要离开我们工作的地方了。我给她出了一道出门考题,同时也叫其他手下一起参加。这题目是这样的:...
2006-03-02 21:49:00 | [匿名:ASP.NET]
#有关动态控件的问题 编辑
那动态控件呢?其一开始是新建对象,处于原始状态,当它被加到父控件的Controls里时,父控件会根据其当前的control阶段来调用该子控件的一些方法,让子控件赶上父控件的control阶段 (这些方法可以从上个贴里leighsword和microhelper贴的Control的AddedControl方法里看到,在此就不重复了,而且也不用看那些方法)。为什么要这样呢?这应该跟整个页面的生命周期有关吧。
2006-01-04 14:00:00 | [匿名:Maple99]
#re: 动态控件的状态问题 编辑
好明显这是个Bug~~
2005-10-25 10:55:00 | [匿名:ouhaojie]
#思归的“动态控件的状态问题”的分析 编辑
Ping Back来自:blog.csdn.net
2004-11-25 09:06:00 | [匿名:gxh973121]
#re: 动态控件的状态问题 编辑
ddlStatic肯定是不用ViewState的,我用Reflector看了asp.net编译aspx生成的临时程序集,ddlStatic的所有ListItem都是在程序中生成的。
2004-10-21 09:52:00 | [匿名:于晓松]
#re: 动态控件的状态问题 编辑
用Paul Wilson的ViewState Parser 可用于繁体吗?为可我的得到不到查看结果------无效字串
2004-10-21 08:26:00 | [匿名:swzlxm(守望者)]
#re: 动态控件的状态问题 编辑
各位说的我都看到了,可是有一个问题还是没人说清楚:
为什么
form1.Controls.Add(ddlDynamic);

ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));
的执行先后顺序不同会对SaveViewState造成影响,为什么?有人找到吗???
2004-10-21 08:13:00 | [匿名:于晓松]
#re: 动态控件的状态问题 编辑
其实这个问题真正tricky的地方不在于什么control life cycle,而是在于ControlCollection.Add()时会对被加Control的viewstate处理产生影响……

我个人觉得这样的design不太make sense,至少从上面的许多回复来看,很多用户可能很难跟踪到问题的真正根源……而文档上的说明又很有限……

很高兴能看到这么高质量的问题 =)
2004-10-21 01:15:00 | [匿名:Wang Ting]
#re: 动态控件的状态问题 编辑
终于又看到思归了 ;D
2004-10-20 22:33:00 | [匿名:不至于]
#re: 动态控件的状态问题 编辑
并不是在Page.Controls.Add中直接调用DropDownList.SaveViewState方法

控件生命周期如下
Instantiate
Initialize
Begin Tracking View State
Load View State (postback only)
Load Postback Data (postback only)
Load
。。。

当调用Page.Controls.Add()动态的添加一个DropDownList时,因为Controls是ControlCollection,所以
实际是调用ControlCollection对象的Add方法

在ControlCollection对象的Add方法中,先做一些常规的检查,分配内存等工作,然后调用容器控件的
AddedControl(Control control, int index)方法,将子控件添加到容器控件中。

在AddedControl中会对子控件的ViewState做一些处理,详情可以参考.Net Reflector反编译的代码

。。。
control.InitRecursive(control1);
if (this._controlState >= ControlState.ViewStateLoaded)
{
object obj1 = null;
if (this._controlsViewState != null)
{
obj1 = this._controlsViewState[index];
this._controlsViewState.Remove(index);
}
control.LoadViewStateRecursive(obj1);
if (this._controlState >= ControlState.Loaded)
{
control.LoadRecursive();
if (this._controlState >= ControlState.PreRendered)
{
control.PreRenderRecursiveInternal();
}
}
。。。

子控件会依次经历以下历程
Instantiate
Initialize
Begin Tracking View State
Load View State (postback only)
Load Postback Data (postback only)
Load
。。。
2004-10-20 20:53:00 | [匿名:microhelper]
#re: 动态控件的状态问题 编辑
看看call stack吧。System.Web.UI.WebControls.ListItemCollection.TrackViewState()就是这么通过ControlCollection.Add被调用的:

019cf8b0 06538fd0 [DEFAULT] [hasThis] Void System.Web.UI.WebControls.ListItemCollection.TrackViewState()
019cf8b4 06538fbe [DEFAULT] [hasThis] Void System.Web.UI.WebControls.ListControl.TrackViewState()
019cf8bc 06538e53 [DEFAULT] [hasThis] Void System.Web.UI.Control.InitRecursive(Class System.Web.UI.Control)
019cf8d8 0653758a [DEFAULT] [hasThis] Void System.Web.UI.Control.AddedControl(Class System.Web.UI.Control,I4)
019cf8f4 06537462 [DEFAULT] [hasThis] Void System.Web.UI.ControlCollection.Add(Class System.Web.UI.Control)
019cf904 063c06fc [DEFAULT] [hasThis] Void WebApplication37.WebForm3.Page_Load(Object,Class System.EventArgs)
at [+0x13c] [+0x8c] c:\inetpub\wwwroot\webapplication37\webform3.aspx.cs:36
019cf944 065391a4 [DEFAULT] [hasThis] Void System.Web.UI.Control.OnLoad(Class System.EventArgs)
019cf954 065390ec [DEFAULT] [hasThis] Void System.Web.UI.Control.LoadRecursive()
019cf968 065384a4 [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequestMain()
019cf9ec 06537167 [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequest()
019cfa28 06536943 [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequest(Class System.Web.HttpContext)
019cfa30 0653691c [DEFAULT] [hasThis] Void System.Web.HttpApplication/CallHandlerExecutionStep.System.Web.HttpApplication+IExecutionStep.Execute()
019cfa40 06271f60 [DEFAULT] [hasThis] Class System.Exception System.Web.HttpApplication.ExecuteStep(Class IExecutionStep,ByRef Boolean)
019cfa88 06271a5b [DEFAULT] [hasThis] Void System.Web.HttpApplication.ResumeSteps(Class System.Exception)
019cfad0 0627192b [DEFAULT] [hasThis] Class System.IAsyncResult System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(Class System.Web.HttpContext,Class System.AsyncCallback,Object)
019cfaec 05f79c77 [DEFAULT] [hasThis] Void System.Web.HttpRuntime.ProcessRequestInternal(Class System.Web.HttpWorkerRequest)
019cfb28 05f79828 [DEFAULT] Void System.Web.HttpRuntime.ProcessRequest(Class System.Web.HttpWorkerRequest)
019cfb34 05f7639d [DEFAULT] [hasThis] I4 System.Web.Hosting.ISAPIRuntime.ProcessRequest(I,I4)
019cfbec 79240d05 [FRAME: ContextTransitionFrame]
019cfcc8 79240d05 [FRAME: ComMethodFrame]
2004-10-20 18:51:00 | [匿名:Wang Ting]
#re: 动态控件的状态问题 编辑
我也用Reflector看了半天,只能看出DropDownList是将列表项存储在ViewState里面,可是怎么也没有找到Page.Controls.Add()和DropDownList.SaveViewState的关系,因为还没弄清ListItem.SaveViewState和ListItem.TrackViewState之间的关系。
2004-10-20 17:10:00 | [匿名:于晓松]
#re: 动态控件的状态问题 编辑
再补充。。。"source code"我指的是Lutz Roeder's .Net Reflector弄出来的"source code"...
2004-10-20 15:38:00 | [匿名:Wang Ting]
#re: 动态控件的状态问题 编辑
It is suck.
2004-10-20 15:22:00 | [匿名:重粒子]
#re: 动态控件的状态问题 编辑
补充。。只有调用过ListItemCollection的IStateManager.TrackViewState()方法以后,添加到ListItemCollection的ListItem的ViewState才会被写到page上……

作者你是怎么看这个问题的?
2004-10-20 15:19:00 | [匿名:Wang Ting]
#re: 动态控件的状态问题 编辑
这个问题的关键是ListItemCollection的IStateManager.TrackViewState()有没有被调用过……

对于page 2,只要用下面的code就一样work了:

((IStateManager)(ddlDynamic.Items)).TrackViewState();

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

这个TrackViewState()方法在调用form1.Controls.Add(ddlDynamic); 时会被自动调用……如果作者没研究过source code也能知道问题的答案,那我就佩服你了……
2004-10-20 15:13:00 | [匿名:Wang Ting]
#re: 动态控件的状态问题 编辑
和控件执行顺序有关吧,控件应该先添加到程序集里,才可以调用的。故TestDyn2.aspx错。
2004-10-20 14:13:00 | [匿名:tjj]
#re: 动态控件的状态问题 编辑
~~~呵呵。。那么我就等着对答案好了。
2004-10-20 11:26:00 | [匿名:Lostinet]
#re: 动态控件的状态问题 编辑
页面初始化 Init
加载视图状态 LoadViewState
处理回发数据
加载页面 Load
回发更改通知
处理回发事件
页面显示前阶段 PreRender
保存视图状态 SaveViewState
显示页面 Render
卸载页面 Unload

TestDyn2 页面在LoadViewState 这一步应该没有载入动态的控件的值吧。TestDyn1 页面是因为 Load 事件 在SaveViewState 之前执行。
2004-10-20 11:21:00 | [匿名:peterG]
#re: 动态控件的状态问题 编辑
为什么第一个就可以玩?

form1.Controls.Add(ddlDynamic);

if (!IsPostBack)
{
for (int i=1; i <=3; i++)
ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));
}
2004-10-20 10:18:00 | [匿名:dali]
#re: 动态控件的状态问题 编辑
很好玩,有时间研究一下,现在谁能帮我翻译两个有关出租车的术语(中译英),等着用,谢谢:

车载台
营运合同
发包方
准牵引总质量
半挂车鞍总质量
车辆残值归属

万分感谢!:)
2004-10-20 10:08:00 | [匿名:webdiyer]
#re: 动态控件的状态问题 编辑
第二个显然有问题

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

form1.Controls.Add(ddlDynamic);

回调后不会ddlDynamic.Items.Add
2004-10-20 10:08:00 | [匿名:dali]
#re: 动态控件的状态问题 编辑
我就不整理详明了,因为根本不值得花时间在.net上,ASP.net我只学用了两三个月就做了五六个规模不小的项目。HTC我不要太熟,都是light(包括内容轻量)的控件.

要永远记住:对于搞技术的人来说,简单的东东不值钱。

坚决反对讨论.net.

2004-10-20 09:51:00 | [匿名:leighsword]
#re: 动态控件的状态问题 编辑
这么多编码,还是没有切中要害
2004-10-20 09:48:00 | [匿名:saucer]
#re: 动态控件的状态问题 编辑
"The Return of Saucer"

喜欢看这些article,对我胃口:)
2004-10-20 09:46:00 | [匿名:mvm]
#re: 动态控件的状态问题 编辑
//note that ControlCollection have Call the AddedControl method of control in below
public virtual void Add(Control child)
{
if (child == null)
{
throw new ArgumentNullException("child");
}
if (this._readOnlyErrorMsg != null)
{
throw new HttpException(HttpRuntime.FormatResourceString(this._readOnlyErrorMsg));
}
if (this._controls == null)
{
this._controls = new Control[5];
}
else if (this._size >= this._controls.Length)
{
Control[] controlArray1 = new Control[this._controls.Length * 4];
Array.Copy(this._controls, controlArray1, this._controls.Length);
this._controls = controlArray1;
}
int num1 = this._size;
this._controls[num1] = child;
this._size++;
this._version++;
this._owner.AddedControl(child, num1);
}
//Control

protected internal virtual void AddedControl(Control control, int index)
{
if (control._parent != null)
{
control._parent.Controls.Remove(control);
}
control._parent = this;
control._page = this._page;
Control control1 = this.flags[0x80] ? this : this._namingContainer;
if (control1 != null)
{
control._namingContainer = control1;
if ((control._id == null) && !control.flags[0x40])
{
control.GenerateAutomaticID();
}
else if ((control._id != null) || (control._controls != null))
{
control1.DirtyNameTable();
}
}
if (this._controlState >= ControlState.ChildrenInitialized)
{
control.InitRecursive(control1);
if (this._controlState >= ControlState.ViewStateLoaded)
{
object obj1 = null;
if (this._controlsViewState != null)
{
obj1 = this._controlsViewState[index];
this._controlsViewState.Remove(index);
}
control.LoadViewStateRecursive(obj1);
if (this._controlState >= ControlState.Loaded)
{
control.LoadRecursive();
if (this._controlState >= ControlState.PreRendered)
{
control.PreRenderRecursiveInternal();
}
}
}
}
}

internal void InitRecursive(Control namingContainer)
{
if (this._controls != null)
{
if (this.flags[0x80])
{
namingContainer = this;
}
string text1 = this._controls.SetCollectionReadOnly("Parent_collections_readonly");
int num1 = this._controls.Count;
for (int num2 = 0; num2 < num1; num2++)
{
Control control1 = this._controls[num2];
control1._namingContainer = namingContainer;
if (((namingContainer != null) && (control1._id == null)) && !control1.flags[0x40])
{
control1.GenerateAutomaticID();
}
control1._page = this._page;
control1.InitRecursive(namingContainer);
}
this._controls.SetCollectionReadOnly(text1);
}
if (this._controlState < ControlState.Initialized)
{
this._controlState = ControlState.ChildrenInitialized;
this.OnInit(EventArgs.Empty);
this._controlState = ControlState.Initialized;
}
this.TrackViewState();
}
internal void LoadRecursive()
{
if (this._controlState < ControlState.Loaded)
{
this.OnLoad(EventArgs.Empty);
}
if (this._controls != null)
{
string text1 = this._controls.SetCollectionReadOnly("Parent_collections_readonly");
int num1 = this._controls.Count;
for (int num2 = 0; num2 < num1; num2++)
{
this._controls[num2].LoadRecursive();
}
this._controls.SetCollectionReadOnly(text1);
}
if (this._controlState < ControlState.Loaded)
{
this._controlState = ControlState.Loaded;
}
}



2004-10-20 09:38:00 | [匿名:leighsword]
#re: 动态控件的状态问题 编辑
我整理一下现象:

第一次请求两个页面 显示的一样效果,只有 ViewState 不一样。

这说明, 不论哪个代码在前面,生成的 Html 代码都是一样的。

但是,前后的问题,造成 ViewState 的不同。

至于原因,就要看具体是啥造成 ViewState 不一的了。
2004-10-20 09:29:00 | [匿名:ghj1976]
#re: 动态控件的状态问题 编辑
看一下.net的源代码什么都明白了
2004-10-20 09:20:00 | [匿名:leighsword]
#re: 动态控件的状态问题 编辑
我想是TestDyn2.aspx,但也是有点迷惑,于是把代码拷到vs.net里面执行,更是吃惊,原来TestDyn1.aspx是对的.不知道怎么解释,TestDyn2.aspx中连数据项都没有加进去?
2004-10-20 09:07:00 | [匿名:chyich]
#re: 动态控件的状态问题 编辑
应该是第一个对了吧,第二个先操作,再加入到form中,ViewState没存
2004-10-20 08:49:00 | [匿名:qqdao]
#re: 动态控件的状态问题 编辑
I think TestDyn2.aspx is right,isn't it?
2004-10-20 08:31:00 | [匿名:重粒子]
#re: 动态控件的状态问题 编辑
不懂为什么会是这样的结果。难道先添加到form的controls集合里,给dropdown添加的listitem就被放到ViewState里了么?后添加到form的controls集合里,dropdown的listitem就不能被ViewState保存?
2004-10-20 08:00:00 | [匿名:Gigabyte]
对不起,目前本随笔不允许发表新评论.

Powered by: Joycode.MVC引擎 0.5.2.0