最近在做webform开发的时候用到了RadioButtonList的数据绑定的功能。由于SelectedValue属性是支持TwoWay数据绑定的,所以可以使用Bind方法进行双向绑定。虽然在Visual Studio的智能提示中没有出现SelectedValue,但是仍然是可以使用的。在使用Reflector或者ILSpy查看ListControl(RadioButtonList继承自ListControl)的SelectedValue属性就可以证明这一点。
// System.Web.UI.WebControls.ListControl [Bindable(true, BindingDirection.TwoWay), WebCategory("Behavior"), Browsable(false), DefaultValue(""), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Themeable(false), WebSysDescription("ListControl_SelectedValue")] public virtual string SelectedValue ......
所以我创建了用于绑定的数据对象,如下所示
[Serializable] public class CandidateReferee { public System.String Email { get; set; } public Guid? RefereeApproach { get; set; } public System.String Name { get; set; } public System.String Address { get; set; } public System.String Telephone { get; set; } }
接下来我把RadioButtonList放到了GridView TemplateField的ItemTemplate中
<asp:RadioButtonList ID="radApproach" DataSourceID="DataSourceApproach" DataTextField="CodeName" DataValueField="CodeValue" RepeatDirection="Horizontal" runat="server" SelectedValue='<%# Bind("RefereeApproach")%>'> </asp:RadioButtonList>
在数据绑定时我提供的RefereeApproach属性值为null, 但是在程序运行的时候却出现了ArgumentOutOfRangeException
后来经过深入的研究发现,使用ListControl时如果存在了DataSourceID属性的情况下,如果还没有运行到OnPreRender的步骤的话,无论设置什么SelectedValue都不会出错,只有在OnPreRender时调用数据绑定的方法时才去进行验证。而此时它会把SelectedValue的属性值存放在cachedSelectedValue这个field中。如下所示:
[Bindable(true, BindingDirection.TwoWay), WebCategory("Behavior"), Browsable(false), DefaultValue(""), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Themeable(false), WebSysDescription("ListControl_SelectedValue")] public virtual string SelectedValue { get { ...... } set { if (this.Items.Count != 0) { if (value == null || (base.DesignMode && value.Length == 0)) { this.ClearSelection(); return; } ListItem listItem = this.Items.FindByValue(value); bool flag = this.Page != null && this.Page.IsPostBack && this._stateLoaded; if (flag && listItem == null) { throw new ArgumentOutOfRangeException("value", SR.GetString("ListControl_SelectionOutOfRange", new object[] { this.ID, "SelectedValue" })); } if (listItem != null) { this.ClearSelection(); listItem.Selected = true; } } <strong>this.cachedSelectedValue = value;</strong> } }
// System.Web.UI.WebControls.ListControl protected internal override void PerformDataBinding(IEnumerable dataSource) { ...... <strong>if (this.cachedSelectedValue == null) { if (this.cachedSelectedIndex != -1) { this.SelectedIndex = this.cachedSelectedIndex; this.cachedSelectedIndex = -1; } return; }</strong> int num = this.Items.FindByValueInternal(this.cachedSelectedValue, true); if (-1 == num) { throw new ArgumentOutOfRangeException("value", SR.GetString("ListControl_SelectionOutOfRange", new object[] { this.ID, "SelectedValue" })); } if (this.cachedSelectedIndex != -1 && this.cachedSelectedIndex != num) { throw new ArgumentException(SR.GetString("Attributes_mutually_exclusive", new object[] { "SelectedIndex", "SelectedValue" })); } this.SelectedIndex = num; this.cachedSelectedValue = null; this.cachedSelectedIndex = -1; }
而且当设置的属性值为null时,无论在哪个方法中都会忽略并且执行ClearSelection()方法清除已选择的选项。而问题就在这个地方,为什么我使用null属性值进行双向绑定的时候却没有被忽略,而是抛出了ArgumentOutOfRangeException在我调试之后发现,RadioButtonList在调用Bind方法进行数据绑定之后,实际cachedSelectedValue的值并不是null, 而是string.Empty,这让我很是困惑。所以无奈之下只好继续深入研究,以寻找产生这个问题的根本原因。
在这个时候我想到了,asp.net页面第一次执行的时候都要进行预编译,而这个预编译过程中使用到的所有方法都在System.Web.Compilation命名空间下,所以只要在这个地方去寻找就应该能够结果。果然功夫不负有心人,最终发现在System.Web.Compilation.CodeDomUtility的