ListControl数据绑定时所出现的问题

最近在做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的GenerateConvertToString(CodeExpressionvalue)方法中发现编译的过程中将Bind方法转成调用Convert.ToString(object value, IFormatProvider provider),代码如下所示:

internal static CodeExpression GenerateConvertToString(CodeExpression value)
{
  CodeMethodInvokeExpression expression = new CodeMethodInvokeExpression {
    Method = { TargetObject = BuildGlobalCodeTypeReferenceExpression(typeof(Convert)), MethodName = "ToString" }
  };
  expression.Parameters.Add(value);
  expression.Parameters.Add(new CodePropertyReferenceExpression(BuildGlobalCodeTypeReferenceExpression(typeof(CultureInfo)), "CurrentCulture"));
  return expression;
}

 

而恰恰就是在Convert中将null转成了string.Empty而使ListControl无法对其进行忽略的。如下所示

public static string ToString(object value, IFormatProvider provider)
{
  IConvertible convertible = value as IConvertible;
  if (convertible != null) return convertible.ToString(provider);
  IFormattable formattable = value as IFormattable;
  if (formattable != null) return formattable.ToString(null, provider);
  if (value != null) return value.ToString();
  return string.Empty;
}

注:所有测试和研究均是在.net framework 4.0中进行,对其他版本的framework不知道是否也存在此问题。

posted on 2011-04-21 09:32  Edward.Net  阅读(388)  评论(0编辑  收藏  举报

导航