在论坛上,动态控件好象是永久的话题。大家都知道要动态控件起作用,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上回答过的一个问题改编而来的
留言反馈
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);
}
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;
}
}
这就是asp.net框架之AddedControl方法,和ControlsViewState字段的用意所在。
这样说就不对了吧。讨论之后更需要有人能透彻的分析一下,比如Lostinet。毕竟大家都想知道为什么,不是吗?
在论坛上,动态控件好象是永久的话题。大家都知道要动态控件起作用,PostBack时需要重新生成或装载(LoadControl),而且需要深入了解其状态的变化过程。
有个同事另谋高就,要离开我们工作的地方了。我给她出了一道出门考题,同时也叫其他手下一起参加。这题目是这样的:...
为什么
form1.Controls.Add(ddlDynamic);
和
ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));
的执行先后顺序不同会对SaveViewState造成影响,为什么?有人找到吗???
我个人觉得这样的design不太make sense,至少从上面的许多回复来看,很多用户可能很难跟踪到问题的真正根源……而文档上的说明又很有限……
很高兴能看到这么高质量的问题 =)
控件生命周期如下
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
。。。
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]
作者你是怎么看这个问题的?
对于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也能知道问题的答案,那我就佩服你了……
加载视图状态 LoadViewState
处理回发数据
加载页面 Load
回发更改通知
处理回发事件
页面显示前阶段 PreRender
保存视图状态 SaveViewState
显示页面 Render
卸载页面 Unload
TestDyn2 页面在LoadViewState 这一步应该没有载入动态的控件的值吧。TestDyn1 页面是因为 Load 事件 在SaveViewState 之前执行。
form1.Controls.Add(ddlDynamic);
if (!IsPostBack)
{
for (int i=1; i <=3; i++)
ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));
}
车载台
营运合同
发包方
准牵引总质量
半挂车鞍总质量
车辆残值归属
万分感谢!:)
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
要永远记住:对于搞技术的人来说,简单的东东不值钱。
坚决反对讨论.net.
喜欢看这些article,对我胃口:)
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;
}
}
第一次请求两个页面 显示的一样效果,只有 ViewState 不一样。
这说明, 不论哪个代码在前面,生成的 Html 代码都是一样的。
但是,前后的问题,造成 ViewState 的不同。
至于原因,就要看具体是啥造成 ViewState 不一的了。