今天看一段代碼,發現對asp.net頁面週期還是不熟悉:
<%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { if (IsPostBack) { int txtCount = int.Parse(txtTextCount.Text); CreateTextBoxList(txtCount); } } private void CreateTextBoxList(int num) { HtmlGenericControl div; HtmlGenericControl span; TextBox txt; RegularExpressionValidator rev; for (int i = 0; i < num; i++) { //创建div div = new HtmlGenericControl(); div.TagName = "div"; div.ID = "divTextBox" + i.ToString(); div.Attributes["class"] = "item2"; //创建span span = new HtmlGenericControl(); span.ID = "spanTextBox" + i.ToString(); span.InnerHtml = "Url Address" + (i + 1).ToString() + ":"; //创建TextBox txt = new TextBox(); txt.ID = "txt" + i.ToString(); txt.CssClass = "input"; //创建格式验证控件,并且将其关联到对应的TextBox rev = new RegularExpressionValidator(); rev.ID = "rev" + i.ToString(); rev.ControlToValidate = txt.ID; rev.Display = ValidatorDisplay.Dynamic; rev.ValidationGroup = "ShowListContent"; rev.ValidationExpression = @"(http(s)?://)?([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?"; rev.ErrorMessage = "Invalid url Address!"; //添加控件到容器 div.Controls.Add(span); div.Controls.Add(txt); div.Controls.Add(rev); divControls.Controls.Add(div); } } protected void btnCreate_Click(object sender, EventArgs e) { txtTextCount.Enabled = false; btnCreate.Enabled = false; } protected void btnOK_Click(object sender, EventArgs e) { TextBox txt; StringBuilder sbResult = new StringBuilder(); int txtCount = int.Parse(txtTextCount.Text); //遍历获取动态创建的TextBox们中的Text值 for (int i = 0; i < txtCount; i++) { //注意:这里必须通过上层容器来获取动态创建的TextBox,才能获取取ViewState内容 txt = divControls.FindControl("txt" + i.ToString()) as TextBox; if (txt != null && txt.Text.Trim().Length > 0) { sbResult.AppendFormat("Url Address{0}: {1}.<br />", i + 1, txt.Text.Trim()); } } divMessage.InnerHtml = sbResult.ToString(); } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title></title> <style type="text/css"> .item { margin: 10px; border-bottom: solid 1px #CCC; } .item2 { margin: 5px; } .input { 200px; } </style> </head> <body> <form id="form2" runat="server"> <div> <asp:HiddenField ID="hid" runat="server" /> <div class="item"> Please input a number: <asp:TextBox runat="server" CssClass="item" ID="txtTextCount" AutoPostBack="True"></asp:TextBox> <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="txtTextCount" ValidationGroup="CreateTextBox" Display="Dynamic" ErrorMessage="Required to input content!"></asp:RequiredFieldValidator> <asp:RegularExpressionValidator ID="RegularExpressionValidator1" ControlToValidate="txtTextCount" ValidationGroup="CreateTextBox" Display="Dynamic" runat="server" ErrorMessage="Only number is valid!" ValidationExpression="^\d+$"></asp:RegularExpressionValidator> <asp:Button runat="server" ID="btnCreate" Text="Create TextBox List" ValidationGroup="CreateTextBox" OnClick="btnCreate_Click" /> <asp:Button runat="server" ID="btnOK" Text="Get TextBox Content" ValidationGroup="ShowListContent" OnClick="btnOK_Click" /> </div> <div runat="server" id="divControls" class="item"></div> <div runat="server" id="divMessage"> </div> </div> </form> </body> </html>
当我点击按钮的时候(按钮没有任何事件处理代码),页面被PostBack
而由于Page_Load()中设置了if (!Page.IsPostBack),所以这两个控件并没有被重新创建
我想问一下,应该如何保持住这两个控件的状态呢?
重新创建一次控件应该不是很好的方法,因为所有控件的数据都要重新绑定,在我这边要动态创建很多这样的控件,并且还要做很多从数据库提取并绑定数据的动作
是否能够在控件第一次被动态创建的时候就使用ViewState或其他方法将他们保持住呢?
请大家指点
首先必须去掉 IsPostBack 的判断你的程序才能正确执行。
为什么那些asp.net入门书上有很多例子在page_load里边写IsPostBack 判断呢?因为asp.net首先将在页面设计器上写的那些控件装入页面,然后才执行page_load也就是执行那些赋值逾矩。而你的代码是创建控件之后首先赋值,然后才装入页面。
当你把控件装入页面的时候,asp.net会自动将thelabel、theTB的状态(例如你原来设置的字体、背景色、内容等等)以及客户端提交的客户输入的内容给你“回填”到控件上,并且准备好触发应该触发事件(在page_load结束之后就会触发)。因此,你对thelabel、theTB的改变会被asp.net刷新,不写IsPostBack也根本不会出错。反而,如果你写了IsPostBack,asp.net根本无法在页面回发的时候在同样位置找到thelabel、theTB控件了,当然无法回填任何数据。
那么那些入门书上的那种代码,是在asp.net对控件处理完(添加入页面)之后,因此你写的代码会刷新asp.net回填的值,因此需要IsPostBack来决定到底维持哪一个值。
而对于DataGrid(GridView)等,由于它在状态中根本不保持数据内容(TextBox会自动保持text属性,但是表格并不自动保持内容数据,这是有差别的),所以比必须重新绑定数据,这也是一种不能使用IsPostBack的情况。
最后,要想在说清楚一些,我把你的代码用asp.net处理设计器上控件的那种固有的那种逻辑改一下(我没有测试是否正确,你理解意思就好):
protected void Page_Load(object sender, EventArgs e) { Label thelabel = new Label(); TextBox theTB = new TextBox(); form1.Controls.AddAt(0, thelabel); form1.Controls.AddAt(1, theTB); if (!Page.IsPostBack) { thelabel.Text = "哈哈哈"; theTB.Text = "嘿嘿嘿"; } }
顺便说一下,看上去这个问题解决了。但是实际上这并没有透彻解决asp.net软件设计问题。
好的、可以长期利用的软件是组件化的,而不是按照页面来设计的。通常对一些数据源(例如一个SQL语句或者一个数据库记录的主键值)输入参数设计一些应用控件,例如根据“栏目”的id号自动产生本栏目的导航列表,并且根据一个“风格”参数生成横向、纵向、平铺、树形等多种形式,这种应用控件才是软件ui设计的真正核心技术,是你公司产品背后的真正英雄。当你需要写一个页面的时候,把业务控件拿过来根据用户的喜好随意摆放、设置一下简单的几个参数,一个应用程序应该在十几分钟之内产生出来。
这时候,控件装入数据的时机根本不能考虑放到page_load中去。而是要根据控件的机制去设计。
我现在如果正规地去写个商业程序,已经几乎根本不在页面上放任何东西,整个页面中仅仅放一个控件。我已经习惯使用控件来看软件开发问题。我也不会允许程序员从页面角度去写涉及需求和应用领域数据的内容,而是要求程序员在其它project里写好应用程序控件——至少是一个用户控件,然后我拿过来“随心所欲”地组合进简单的页面里。所以楼主的问题我其实并不会遇到。
this.PlaceHolder1.Controls.Add(new Label());
if (!this.IsPostBack)
((Label)this.PlaceHolder1.Controls[0]).Text = "haha";
然后让页面上有提交按钮,并没有丢失这个Text的值。你还是在后台那个“if(”语句设个断点,看看Label的EnableViewState、Text的值有什么异常。
我要说明一下,绝对不能放在 onInit 相关的阶段去创建动态控件。因为之所以使用动态空间,往往就是为了根据页面上的实际ViewState(这些专门记录状态的机制)来选择插入的控件的。而Init阶段还没有回填ViewState。那些在页面设计器上控件之所以可以自动产生在init阶段创建,是因为他们本身就是傻瓜的,不是动态的。