动态控件、DropDownList和ViewState

最近碰到这样一个问题,可以描述为:


我要在页面上找到指定的DropDownList,并在该DropDownList的前面加入一个动态创建的RequiredFieldValidator


之所以要加在前面是可以使errormessage出现在控件的上方


DropDownList 的 Item不是在aspx中用asp:ListItem写死,而是通过DropDownList.Items.Insert或者DataBind在后台添加


(至于如何指定DropDownList,用的是Attribute,在这里就不多说了)


Page_Load()中给DropDownList添加项的代码:


ddl1.Items.Insert(0,new ListItem(“”, “”));
ddl1.Items.Insert(1,new ListItem(“1”, “1”));
ddl1.Items.Insert(2,new ListItem(“2”, “2”));


Page_Load()中插入动态控件的代码:


targetControl = “ddl1”;    // targetControl, the id of the target dropdownlist
errorMessage = “Must choose one.”;


RequiredFieldValidator rfv = new RequiredFieldValidator();
rfv.ID = targetControl + “RequiredFieldValidator”;
rfv.ControlToValidate = targetControl;   
rfv.ErrorMessage = errorMessage;
rfv.Display = ValidatorDisplay.Dynamic;
rfv.EnableViewState = false;


controlToValidate = FindControl(targetControl);
container = controlToValidate.Parent;
index = container.Controls.IndexOf(controlToValidate);
container.Controls.AddAt(index,rfv);


错误是:无论如何选择,DropDownList提交上来的值始终是空


研究发现,动态绑定的DropDownList在PostBack后是通过ViewState取值的,而只要动态插入的控件的位置在DropDownList之前,即使我把动态控件的EnableViewState设为false,PostBack后就会出现”Failed to load viewstate”或者控件从viewstate中取到的值为空的错误。


我的解决方法是:


1.用Controls.Add把动态控件插到DropDownList之后,也就是Validator的index比DropDownList大,就不会影响DropDownList从viewstate取值


2.如果一定要放在前面,可以把上面两段代码的任意一段放到OnInit中


对此我的理解是:


1.页面的ViewState是在Page_Load以前生成,所以Page_Load中动态加入的控件不改变当前ViewState,但改变了当前页控件的顺序,所以在PostBack后读取ViewState到对应的控件出错。


2.动态绑定DropDownList的代码放在Page_Load中,提交后页面从ViewState中取得保存的值,如果页面禁用ViewState就只好放到OnInit中,每次刷新后重新Init。