实际上Eval方法是TemplateControl的,而System.Web.UI.Page和System.Web.UI.UserControl都继承于TemplateControl,所以我们可以在Page和UserControl上直接调用个方法。
Page.Eval方法可以帮助我们更好的撰写数据绑定表达式,在ASP.NET 1.x时代,数据绑定表达式的一般形式是:
<%# DataBinder.Eval( Container , “DataItem.Name”) %>
而在ASP.NET 2.0中,同样的代码,我们可以这样写:
<%# Eval( “Name” )%>
ASP.NET 2.0是怎么实现的呢?我们先从Eval方法来研究,通过反射.NET Framework 2.0类库的源代码,我们可以看到这个方法是这样实现的:
protected internal object Eval(string expression)
{
this.CheckPageExists();
return DataBinder.Eval(this.Page.GetDataItem(), expression);
}
第一行我们不必管,这是检查调用的时候有没有Page对象的,如果没有则会抛出一个异常。
关键是第二行:
return DataBinder.Eval(this.Page.GetDataItem(), expression);
Page.GetDataItem()也是2.0中新增的一个方法,用途是正是取代ASP.NET 1.x中的Container.DataItem。
看来不摸清楚GetDataItem()方法,我们也很难明白Eval的原理。GetDataItem的实现也很简单:
public object GetDataItem()
{
if ((this._dataBindingContext == null) || (this._dataBindingContext.Count == 0))
{
throw new InvalidOperationException(SR.GetString("Page_MissingDataBindingContext"));
}
return this._dataBindingContext.Peek();
}
我们注意到了有一个内部对象_dataBindingContext,通过查源代码发现这是一个Stack类型的东西。所以他有Peek方法。而这一段代码很容易看懂,先判断这个Stack是否被实例化,然后,判断这个Stack里面是不是有任何元素,如果Stack没有被实例化或者没有元素则抛出一个异常。最后是将这个堆栈顶部的元素返回。
ASP.NET 2.0用了一个Stack来保存所谓的DataItem,我们很快就查到了为这个堆栈压元素和弹出元素的方法:Control.DataBind方法:
protected virtual void DataBind(bool raiseOnDataBinding)
{
bool flag1 = false;//这个标志的用处在上下文中很容易推出来,如果有DataItem压栈,则在后面出栈。
if (this.IsBindingContainer)//判断控件是不是数据绑定容器,实际上就是判断控件类是不是实现了INamingContainer
{
bool flag2;
object obj1 = DataBinder.GetDataItem(this, out flag2);//这个方法是判断控件是不是有DataItem属性,并把它取出来。
if (flag2 && (this.Page != null))//如果控件有DataItem
{
this.Page.PushDataBindingContext(obj1);//把DataItem压栈,PushDataBindingContext就是调用_dataBindingContext的Push方法
flag1 = true;
}
}
try
{
if (raiseOnDataBinding)//这里是判断是不是触发DataBinding事件的。
{
this.OnDataBinding(EventArgs.Empty);
}
this.DataBindChildren();//对子控件进行数据绑定,如果这个控件有DataItem,则上面会将DataItem压入栈顶,这样,在子控件里面调用Eval或者GetDataItem方法,就会把刚刚压进去的DataItem给取出来。
}
finally
{
if (flag1)//如果刚才有压栈,则现在弹出来。
{
this.Page.PopDataBindingContext();//PopDataBindingContext就是调用_dataBindingContext的Pop方法
}
}
}
至此,我们已经可以完全了解ASP.NET 2.0中GetDataIten和Eval方法运作的原理了,下一次我打算研究ASP.NET 2.0中的新的Bind语法。
see http://localhost/QuickStartv20/aspnet/doc/data/templates.aspx#twowaybind
双向数据绑定
与 DetailsView 控件一样,FormView 通过其关联的数据源控件支持自动 Update、Insert 和 Delete 操作。若要定义编辑或插入操作的输入 UI,可在定义 ItemTemplate 的同时定义 EditItemTemplate 或 InsertItemTemplate。在本模板中,您将对输入控件(如 TextBox、CheckBox 或 DropDownList)进行数据绑定,以绑定到数据源的字段。但是,这些模板中的数据绑定使用“双向”数据绑定语法,从而允许 FormView 从模板中提取输入控件的值,以便传递到数据源。这些数据绑定使用新的 Bind(fieldname) 语法而不是 Eval。
重要事项: 使用 Bind 语法进行数据绑定的控件必须设置有 ID 属性。
<asp:FormView DataSourceID="ObjectDataSource1" DataKeyNames="PhotoID" runat="server">
<EditItemTemplate>
<asp:TextBox ID="CaptionTextBox" Text='<%# Bind("Caption") %>' runat="server"/>
<asp:Button Text="Update" CommandName="Update" runat="server"/>
<asp:Button Text="Cancel" CommandName="Cancel" runat="server"/>
</EditItemTemplate>
<ItemTemplate>
<asp:Label Text='<%# Eval("Caption") %>' runat="server" />
<asp:Button Text="Edit" CommandName="Edit" runat="server"/>
</ItemTemplate>
</asp:FormView>
在对 GridView 或 DetailsView 执行更新或插入操作时,如果该控件的列或字段定义了 BoundField,GridView 或 DetailsView 负责创建 Edit 或 Insert 模式中的输入 UI,以便它能自动提取这些输入值以传递回数据源。由于模板包含任意的用户定义的 UI 控件,因此,需要使用双向数据绑定语法,这样 FormView 等模板化控件才能知道应从模板中提取哪些控件值以用于更新、插入或删除操作。在 EditItemTemplate 中仍然可以使用 Eval 语法进行不传递回数据源的数据绑定。另请注意,FormView 与 DetailsView 和 GridView 一样,支持使用 DataKeyNames 属性保留主键字段(即使这些字段并未呈现)的原始值以传递回更新/插入操作。
FormView 支持使用 DefaultMode 属性指定要显示的默认模板,但在默认情况下,FormView 以 ReadOnly 模式启动并呈现 ItemTemplate。若要启用用于从 ReadOnly 模式转换为 Edit 或 Insert 模式的 UI,可以向模板添加一个 Button 控件,并将其 CommandName 属性设置为 Edit 或 New。可以在 EditItemTemplate 内添加 CommandName 设置为 Update 或 Cancel 的按钮,以用于提交或中止更新操作。类似地,也可以添加 CommandName 设置为 Insert 或 Cancel 的按钮,以用于提交或中止插入操作。
下面的示例演示一个定义了 ItemTemplate 和 EditItemTemplate 的 FormView。ItemTemplate 包含使用 Eval(单向)绑定的控件,而 EditItemTemplate 包含一个使用 Bind 语句双向绑定的 TextBox 控件。主键字段 (PhotoID) 是使用 DataKeyNames 属性在视图状态中进行往返的。FormView 包含用于在其模板之间进行切换的命令按钮。
C# Two-Way Databinding in a FormView Edit Template
通过使用添加到 Columns 或 Fields 集合的 TemplateField,GridView 和 DetailsView 还支持模板化 UI。TemplateField 支持使用 ItemTemplate、EditItemTemplate 和 InsertItemTemplate(仅 DetailsView)指定这些控件的不同呈现模式中的字段 UI。与上面的 FormView 示例一样,EditItemTemplate 或 InsertItemTemplate 中的双向绑定允许 GridView 或 DetailsView 从这些模板中的控件提取值。TemplateField 的常见用途是向 EditItemTemplate 添加验证程序控件,用于 GridView 或 DetailsView 操作的声明性验证。下面的示例演示这种方法的一个示例。有关 ASP.NET 中可用的验证控件的更多信息,请参考本教程的“验证窗体输入控件”部分。
C# Validation in a GridView Edit Template
TemplateField 的另一个用途是自定义输入控件,这种控件用于输入 GridView 或 DetailsView 列/字段值。例如,可以将 DropDownList 控件放在 TemplateField 的 EditItemTemplate 中,以允许从预定义的值列表进行选择。下面的示例演示这种方法。注意,本例中的 DropDownList 与它自己的数据源控件进行了数据绑定,以便动态地获取该列表的值。
C# DropDownList in a GridView Edit Template
ASP.NET 2.0 改进了模板中的数据绑定,将数据绑定语法由完整的 v1.x 语法 DataBinder.Eval(Container.DataItem, fieldname) 简化为 Eval(fieldname)。与 DataBinder.Eval 一样,Eval 方法也接受一个可选的 formatString 参数。缩写的 Eval 语法与 DataBinder.Eval 的不同之处,在于 Eval 根据最近的容器对象(在上面的示例中为 DataListItem)的 DataItem 属性自动解析字段,而 DataBinder.Eval 接受该容器的一个参数。因此,Eval 仅在数据绑定控件的模板中使用,而不能在“页”级别使用。当然,ASP.NET 2.0 页继续支持 DataBinder.Eval,因此它可用于简化的 Eval 语法不受支持的场合。 <asp:DataList DataSourceID="ObjectDataSource1" runat="server">
<ItemTemplate>
<asp:Image ImageUrl='<%# Eval("FileName", "images/thumbs/{0}") %>' runat="server"/>
<asp:Label Text='<%# Eval("Caption") %>' runat="server"/>
</ItemTemplate>
</asp:DataList>