要理解第一个问题,即,对于一个动态生成的DropDownList对象,为什么先添加ListItem,后调用父控件的Controls.Add,其状态并没有保存,其关键在于理解TrackViewState的调用以及动态控件加入父控件的Controls后的阶段“追赶”过程。
如果你在文档里查询Control的TrackViewState方法描述,其中说到,只有调用该方法后,view-state的变化才会存到服务器控件的StateBag对象里去,这样才会在下一次的PostBack后的LoadViewState中恢复到原来状态。
大家都知道每个控件一般都会经历如下几个阶段 (抄自《Developing Microsoft ASP.NET Server Controls and Components》 一书第九章)
1。Instantiate
2。Initialize
3。Begin Tracking View State
4。Load View State (postback only)
5。Load Postback Data (postback only)
6。Load
7。Raise Changed Events (postback only, optional)
8。Raise Postback Events (postback only, optional)
9。PreRender
10。SaveViewState
11。Render
12。Unload
13。Dispose
在页面里declared的服务器控件,譬如,例子中的ddlStatic,其TrackViewState方法是在Init阶段后面调用的,其后的变化将保存到StateBag里去,但其前的变化不会保存。如果你用Paul Wilson的ViewState Parser查看该例的ViewState,你是看不到其状态的。但假如在此之后,你改变其状态,那么这些状态也许就会保存到ViewState去(取决于该对象是否override了SaveViewState)。
那动态控件呢?其一开始是新建对象,处于原始状态,当它被加到父控件的Controls里时,父控件会根据其当前的control阶段来调用该子控件的一些方法,让子控件赶上父控件的control阶段 (这些方法可以从上个贴里leighsword和microhelper贴的Control的AddedControl方法里看到,在此就不重复了,而且也不用看那些方法)。为什么要这样呢?这应该跟整个页面的生命周期有关吧。
打个比方,不是很恰当,但凑合着吧,这好象是复制一个人后,让他快速经历婴儿,童年,少年,青年,。。。直至赶上被复制人目前的阶段为止。
但大概来讲,当我们在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则不会被保存下来。
其实解决TestDyn2.aspx中的问题有个现成的答案,即去除 if (!IsPostBack):
DropDownList ddlDynamic = new DropDownList();
ddlDynamic.ID = "ddlDynamic";
for (int i=1; i <=3; i++)
ddlDynamic.Items.Add(new ListItem(i.ToString(), i.ToString()));
form1.Controls.Add(ddlDynamic);
这样,跟ddlStatic一样,每次都生成ListItem对象
第二个问题,即为什么动态生成的DropDownList控件在PostBack后在Page_Load里其选择的项没有被设置,再供大家研究。
打印 | 张贴于 2004-10-21 11:23:00 | Tag:暂无标签
留言反馈
在论坛上,动态控件好象是永久的话题。大家都知道要动态控件起作用,PostBack时需要重新生成或装载(LoadControl),而且需要深入了解其状态的变化过程。
有个同事另谋高就,要离开我们工作的地方了。我给她出了一道出门考题,同时也叫其他手下一起参加。这题目是这样的:...
private void addRow(int j)
{
for(int x =0;x<j;x++)
{
TableRow newRow = new TableRow();
newRow.BorderWidth=1;
for (int i=0; i<4; i++)
{
TableCell newCell = new TableCell();
newCell.Width=190;
newCell.BorderWidth=1;
TextBox newControl = new TextBox();
newControl.Width=190;
newControl.Text = "单元格 " + i.ToString();
newControl.ID=x.ToString() + i.ToString();
newCell.Controls.Add(newControl);
newRow.Cells.Add(newCell);
}
tabDetails.Rows.Add(newRow);
}
在page_load里调了
addRow(3;)
Button_click
addRow(1)
单击一次按钮后,总共成四行,再也不会增加了,不知道为什么啊?
请帮忙,上面是我的MSN
未能加载视图状态。正在向其中加载视图状态的控件树必须与前一请求期间用于保存视图状态的控件树相匹配。例如,当以动态方式添加控件时,在回发期间添加的控件必须与在初始请求期间添加的控件的类型和位置相匹配。
private void Dg_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
{
if (e.Item.ItemType==ListItemType.EditItem)
{
DropDownList drop=new DropDownList();
drop.ID="drop";
e.Item.Cells[2].Controls.AddAt(0,drop);
drop.Items.Add(new ListItem("下载","下载"));
drop.Items.Add(new ListItem("音乐","音乐"));
drop.Items.Add(new ListItem("伴奏","伴奏"));
drop.Items.Add(new ListItem("普通","普通"));
((TextBox)e.Item.Cells[0].Controls[0]).Width=90;
((TextBox)e.Item.Cells[1].Controls[0]).Width=90;
((TextBox)e.Item.Cells[3].Controls[0]).Width=70;
}
}
然后在更新时
private void Dg_UpdateCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
int index = e.Item.ItemIndex;
int Id =Convert.ToInt32(Dg.DataKeys[index]);
string s1 = ((TextBox)e.Item.Cells[0].Controls[0]).Text;
string s2=((TextBox)e.Item.Cells[1].Controls[0]).Text;//((DropDownList).SelectedItem.Text
string s3=((TextBox)e.Item.Cells[3].Controls[0]).Text;
string i=((DropDownList)e.Item.Cells[2].Controls[0]).SelectedItem.Value;
//此句出错。
}
应该是ViewState问题,但不知道怎么弄,
Process posted values for the second time (in case of dynamically created controls)
private void ProcessRequestMain()
base.LoadRecursive(); //call this.OnLoad(EventArgs.Empty);
if (this.IsPostBack)
{
this.ProcessPostData(this._leftoverPostData, false);
}
System.Web.UI.Page
private void ProcessPostData(NameValueCollection postData, bool fBeforeLoad)
foreach (string text1 in postData)
{
Control control1 = this.FindControl(text1);
IPostBackDataHandler handler1 = (IPostBackDataHandler) control1;
handler1.LoadPostData(text1, this._requestValueCollection);
}
System.Web.UI.WebControls.DropDownList
bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
string[] textArray1 = postCollection.GetValues(postDataKey);
int num1 = this.Items.FindByValueInternal(textArray1[0]);
this.SelectedIndex = num1;
__VIEWSTATE=dDwzNzY3MTE5NzM7dDw7bDxpPDE%2BOz47bDx0PDtsPGk8Nz47PjtsPHQ8dDw7cDxsPGk8MD47aTwxPjtpPDI%2BOz47bDxwPDE7MT47cDwyOzI%2BO3A8MzszPjs%2BPjs%2BOzs%2BOz4%2BOz4%2BOz6Meh4ilGU9r4pekBZyiKLVngVVnA%3D%3D&btn=Click+Me&ddlStatic=1&TextBox1=4534543543&ddlDynamic=2
而用Request.Params["__VIEWSTATE"]取得的值是没有&btn=Click+Me&ddlStatic=1&TextBox1=4534543543&ddlDynamic=2这一部分,页面加载数据时是按这一部分加载的
在FORM_LOAD里改变选项并不会改变这一部分的值,所以在Page_Load里其选择的项没有被设置