数据绑定表达式(上):.NET发现之旅(一) javascript
2009-06-30 10:29:06 来源:网络转载 做者:佚名 共有评论(0)条 浏览次数:859php
做为.NET平台软件开发者,咱们频繁与各类各样的数据交互,这些数据经常来源于文本、自定义类型、XML、数据库等,访问这些数据有不少方法,而数据绑定表达式即是其中最经常使用也是最实用的方法之一。我用2篇博文,尽可能说透。NET平台数据绑定表达的来源,使用方法,底层原理,效率等。另外这2篇博文我最初发表于博客园。html
数据绑定表达式必须包含在<%#和%>字符之间。格式以下:java
<tagprefix:tagname property='<%# data-binding expression %>' runat="server" />sql |
或者以下:数据库
<%# data-binding expression %>express |
ASP.NET 支持分层数据绑定模型,数据绑定表达式使用 Eval 和 Bind 方法将数据绑定到控件,并将更改提交回数据库。编程
Eval 方法是静态单向(只读)方法,因此Eval 函数用于单向(只读)绑定,该方法采用数据字段的值做为参数并将其做为字符串返回。c#
Bind 方法支持读/写功能,因此Bind 函数用于双向(可更新)绑定。该方法能够检索数据绑定控件的值并将任何更改提交回数据库。数组
XPath 方法支持对XML类型的数据源提供支持。
1,能够将数据绑定表达式包含在服务器控件或者普通的html元素的开始标记中属性名/属性值对的值侧。例如:
<asp:TextBox ID="TextBox1" runat="server" Text='<%#数据绑定表达式%>' ></asp:TextBox><br /> |
此时数据的绑顶表达式能够是一个变量,也能够是一个带返回值的c#或者VB.NET方法,还能够是某个控件的某个属性的值,也能够是c#或者VB.NET对象的某个字段或者属性的值等等。固然也能够直接就是一个字符串,例如"hello". 若是此时的数据绑定表达式是Eval("数据库中某个表的某个字段")等,那么必须把TextBox1放在某个循环显示的控件的模板中才正确,不然会提示:Eval()、XPath() 和 Bind() 这类数据绑定方法只能在数据绑定控件的上下文中使用。其实就是想让你把TextBox1放在像Repeater,DataList,GridView这样的控件的模板中。 2,数据绑定绑定表达式包含在在页面中的任何位置。例如:
<form id="form1" runat="server"> <div> <%#Eval("数据绑定表达式1")%> <%#Eval("数据绑定表达式2")%> </div> </form> |
此时数据的绑顶表达式能够是一个变量,也能够是一个带返回值的C#或者VB.NET方法,还能够是某个控件的某个属性的值,也能够是C#或者VB.NET对象的某个字段或者属性的值等等。固然也能够直接就是一个字符串,例如"hello".
若是此时的数据绑定表达式是Eval("数据库中某个表的某个字段")等,那么必须把<%#Eval("数据绑定表达式1")%> <%#Eval("数据绑定表达式2")%> 放在像Repeater,DataList,GridView这样的控件的模板中。
3,能够将数据绑定表达式包含在Javascript代码中,从而实如今Javascript中调用C#或者VB.NET的方法。例如:
Deafult2.aspx: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>无标题页</title> <script language ="javascript" type="text/javascript"> function GetStr() { var a; a = ''; a='<%#CSharpToJavascript()%>' //调用c#的方法 alert(a); } </script> </head> <body> <form id="form1" runat="server"> <div> <input id="Button1" type="button" value="Javascript调用c#的方法!" onclick="GetStr()" /</div> </form> </body> </html> |
Default2.cs: using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class Default2 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Page.DataBind();//方法有返回值的要先绑定,才能实现Javascript调用c#的方法! } public string CSharpToJavascript() { return "Javascript调用c#的方法!"; } } |
1,能够是一个变量
例如
:<asp:Label ID="Label1" runat="server" Text="<%#变量名%>"></asp:Label> |
2,能够是服务器控件的属性值
例如: <asp:Label ID="Label1" runat="server" Text="<%#TextBox2.Text %>"></asp:Label>
3,能够是一个数组等集合对象
例如把一个数组绑定到列表控件,例如ListBox等,或者Repeater,DataList,GridView这样的控件等,此时只须要把属性DataSource='<%# 数组名%>' .
4,能够是一个表达式
例如:Person是一个对象,Name和City是它的2个属性,则数据绑定表达式能够这样写: <%#(Person.Name + " " + Person.City)%>.
5,能够是一个方法
例如:<%#GetUserName()%>.GetUserName()是一个已经定义的C#方法,通常要求有返回值。
6,能够是用Eval,DateBinder.Eval取得的数据表的字段,这个是最多见的了,再也不举例。
注意:若是数据绑定表达式做为属性的值,只要数据绑定表达式中没有出现双引号,那么<%#数据绑定表达式%>的最外层用双引号或者单引号均可以。若是数据绑定表达式中出现双引号,则<%#数据绑定表达式%>的最外层最好要用单引号。
,<%#DataBinder.Eval(Container.DataItem,"字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名","{0:c}") %> |
还有2种不经常使用的:
<%# DataBinder.Eval(Container,"DataItem.字段名")%> <%# DataBinder.Eval(Container,"DataItem.字段名",{0:c})%> |
Container.DataItem至关于数据库中某个表或者视图中的一行记录,而一行能够有不少列。
使用三目运算符?:的例子:
<%# DataBinder.Eval(Container.DataItem, "字段名")。ToString()。Trim()。Length>16?DataBinder.Eval(Container.DataItem, "字段名")。ToString()。Trim()。Substring(0,16): DataBinder.Eval(Container.DataItem, "字段名")。ToString()。Trim() %> |
2,<%#Eval("字段名")%>
<%#Eval("字段名","{0:c}")%>
.NET 2.0新出现的一个方法。和DataBinder.Eval()等价。
绑定生日的例子:
<%#string.Format("{0:yyyy-MM-dd dddd}",Eval("stuBirth"))%> <%# DataBinder.Eval(Container.DataItem,"stuBirth","{0:yyyy-MM-dd}")%> |
使用三目运算符的例子:
<%#(Eval("性别"))。ToString() =="True"?"男":"女"%>
性别字段类型为:是/否(Access),bit(sql server)
调用方法的例子:
<%# GetUserPhoto(Eval("PhotoPath")) %> GetUserPhoto()的定义: string GetUserPhoto(object photoPath) { if (photoPath == DBNull.Value)<%#((DataRowView)Container.DataItem)["字段名"] %> 绑定到DataView,DataTable,DataSet { return "<img src='Images/none.gif'>"; } else { return "<img src='Upload/" +photoPath.ToString() + "'>"; } } |
3, <%#((DataRowView)Container.DataItem)["字段名"] %>
<%# string.Format("{0:c}", ((DataRowView)Container.DataItem)["字段名"])%> |
Container.DataItem至关于数据库中某个表或者视图中的一行记录,而一行能够有不少列。
类型转换后相乘的例子:
<%# (int)((DataRowView)Container.DataItem)["字段名1"]*(int) ((DataRowView)Container.DataItem)["字段名2"]%> |
上面三种绑定方法的效率:Eval方法执行时候会调用DataBinder.Eval方法,DataBinder.Eval方法在运行时使用反射执行后期绑定计算,会致使性能明显降低。因此会致使性能明显降低。因此三者中<%#((DataRowView)Container.DataItem)["字段名"] %>的性能最好。
这一节继续来谈。NET中的数据绑定表达式。
本节涉及的内容以下:
一,数据绑定方法的来源以及在低层上的实现。
二,数据绑定方法的执行效率排序。
<%#Container.DataItem%> <%#GetDataItem()%> <%#Eval("字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名")%> <%#((DataRowView)Container.DataItem)["字段名"] %> <%#((Type)Container.DataItem).成员 %> <%#((Type)GetDataItem()).成员 %>
上面七种绑定形式以及它们的变幻形式都用过吗?性能怎么排序?
复习一下:第一节咱们主要谈了数据绑定表达式的各类形式,在ASP.NET页面中出现的位置,以及咱们常绑定到与数据库有关的DataView,DataTable,DataSet 等数据源的数据绑定表达式的各类形式。
你有没有对Eval方法和DataBinder.Eval方法好奇过?
在.NET2.0中咱们常常用Eval方法在Repeater,DataList,GridView等循环控件中绑定数据,Eval方法和DataBinder.Eval方法在低层是怎么实现的?它们到底有什么千丝万缕的关系?
咱们经常使用的Eval方法实际上是Page类的一个静态单向只读方法,并且它是一个受保护的方法。实际上Page类的Eval方法是继承自TemplateControl类的。TemplateControl 类是一个抽象类,它为Page 类和 UserControl 类提供通用属性和方法。咱们先来看一下继承家谱:
System.Object System.Web.UI.Control System.Web.UI.TemplateControl System.Web.UI.Page System.Web.UI.UserControl
Eval方法就是TemplateControl类的方法,它有两种形式:
名称 |
说明 |
TemplateControl.Eval (String) |
计算数据绑定表达式。 |
TemplateControl.Eval (String, String) |
使用用于显示结果的指定格式字符串计算数据绑定表达式。 |
事实上TemplateControl类还提供了XPath方法和XPathSelect方法供Page类和UserControl继承。这2个方法是和XML数据源有关的绑定方法。
若是细心的你查看TemplateControl类的基类Control类,你就会发现其实Control类并无提供Eval,XPath,XPathSelect等方法。因此Eval,XPath等方法最终是在TemplateControl类中实现的。
如今,终于找到了Eval,XPath等数据绑定方法的来源了。
Eval,XPath等方法是。NET 2.0新增的方法。在。NET 1.1时代咱们常常用的是DateBinder.Eval方法。形如:
<%#DataBind.Eval(Container.DataItem,"字段名") %>
<%#DataBind.Eval(Container.DataItem,"字段名","{0:c}") %>
Eval的出现其实就是为了简化DataBinder.Eval方法的写法从而代替它。
在ASP.NET 2.0中及以上,当咱们调用Eval时,Eval 方法会使用GetDataItem方法调用DataBinder.Eval方法计算表达式的值。要想理解这句话,就算查边MSDN也一头雾水,除非咱们知道Eval方法的源代码,不然根本找不到蛛丝马迹。这里就要用到反射了。咱们经过反射得到了Eval方法的源代码:
protected internal object Eval(string expression)
{
this.CheckPageExists();
return DataBinder.Eval(this.Page.GetDataItem(), expression);
}
终于见到GetDataItem()方法了,其实它就是Page类的一个方法,也是。NET 2.0新增一个方法。GetDataItem()方法的做用是为了得到Container.DataItem,它是。NET 2.0中用来代替Container.DataItem的,若是你曾经用Repeater和DataList等绑定过数组或者ArrayList等,你就会发现<%#GetDataItem()%>和<%#Container.DataItem%>等价。同时,能够确定:Eval方法在低层上确实是调用DataBinder.Eval方法实现数据绑定的。其中“this.CheckPageExists();” 是检查调用的时候有没有Page对象的,若是没有则会抛出一个异常。
要弄清Eval是怎么工做的,GetDataItem()方法的低层实现咱们也要用反射来获取:
public object GetDataItem()
{
if ((this._dataBindingContext == null) || (this._dataBindingContext.Count == 0))
{
throw new InvalidOperationException(SR.GetString("Page_MissingDataBindingContext"));
}
return this._dataBindingContext.Peek();
}
咱们从GatDataItem()方法中看到“return this._dataBindingContext.Peek();”很快就猜测_dataBindingContext是否是一个堆栈呢?事实它就是一个堆栈!经过反射查看源代码咱们得出:_dataBindingContext是一个Stack类型对象。因此它有Peek方法。“return this._dataBindingContext.Peek(); ”正是把堆栈顶部的元素返回。而if语句是用来判断这个堆栈是否已经存在或者是否已经有元素存在,若是if不成立,就会抛出一个异常。
从上面的分析咱们知道:_dataBindingContext堆栈的做用是经过GetDataItem()方法这个桥梁向Eval方法提供Container.DateItem.用逆向思惟来理解上面这句话:Eval方法能够自动计算出Container.DataItem,缘由就是从dataBindingContext堆栈来获取Container.DataItem;这也就为何Eval方法可以知道形如<%#Eval"字段名"%>中字段名隶属于哪一个数据项的属性的缘由;同时咱们也知道。NET 2.0中的Eval在本质上的实现并无抛弃Container.DataItem,而Container.DataItem在2.0时代也没有消失。
那么_dataBindingContext这个保存Container.DataItem的堆栈是怎么创建的呢?
咱们很快就想到每次绑定控件时候最后那条语句是什么:this.控件ID.DataBind();对就是DataBind()方法,DataBind()方法还有一个重载:DataBind(bool raiseOnDataBinding)。为_dataBindingContext这个堆栈压入元素和弹出元素的方法正是用DataBind(bool flag)这个重载方法实现的。
DataBind(bool raiseOnDataBinding)在低层的实现:
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方法 } } }
当咱们执行到this.控件ID.DataBind();时候。在低层上就会调用这个重载的方法来准备包含DataItem的_DatBindingContext堆栈。
上面的代码中提到了DataBinding事件,那么它通常何时被触发呢?
1,若是用编程方式,那么在咱们调用DataBind()方法时候自动触发DataBinding事件。
2,若是咱们用数据源控件(例如SqlDataSource等),当把控件绑定到数据源控件时候,这个事件就会自动触发。
通常数据绑定表达式经常放在模板中循环显示数据,例如Repeater和DataList等的模板。那么下面这个知识点应该知道:Repeater,DataList,FormView等控件必须使用模板,若是不使用模板,这些控件将没法显示数据。而GridView,DetailsView,Menu等控件也支持模板,但显示数据时不是必须的。而TreeView控件不支持模板。
注意:通常状况下,数据绑定表达式不会自动计算它的值,除非它所在的页或者控件显示调用DataBind()方法。DataBind()方法可以将数据源绑定到被调用的服务器控件及其全部子控件,同时分析并计算数据绑定表达式的值。
终于写的有点眉目了,好累!咱们该回头看看Eval方法调用的静态DataBinder.Eval方法在低层的实现了。我把DataBinder类的源代码做为附近提供下载。
从“一”讲述的低层实现。咱们很容易来排序下面数据绑定表达式的执行效率
<%#Container.DataItem%> <%#GetDataItem()%> <%#Eval("字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名")%> <%#((DataRowView)Container.DataItem)["字段名"] %> <%#((Type)Container.DataItem).成员 %> <%#((Type)GetDataItem()).成员 %>
1,效率最高应该是:
<%#((Type)Container.DataItem).成员 %> <%#Container.DataItem%> <%#((DataRowView)Container.DataItem)["字段名"] %>
2,效率排第二的是:
<%#((Type)GetDataItem()).成员 %> <%#GetDataItem()%>
3,效率最低的是:
<%#Eval("字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名")%>
其实按上面的排序有失公允,缘由是这七种数据表达绑定形式运用的场合不是彻底相同的。
使用场合大概以下:
1, |<%#Eval("字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名")%>
它们的使用场合最广,数据源能够为与数据库有关的DataSet,DataTable,DataView.也能够为普通集合(例如:数组,ArrayList,HashTable等)和泛行集合(例如:List<T>,Dictionary<Tkey,Tvalue>等)。
注:它们2个永远能够相互替换,至少目前是这样,凡是能够用Eval方法的地方,就能够用DataBinder.Eval方法替换。从低层实现上,Eval比DataBinder.Eval方法效率稍低,缘由是Eval方法对了调用GetDataItem()方法这一步。但最终都是经过DataBinder.Eval方法利用反射技术根据名称查找属性,从而计算出表达式的值,因此很是影响性能。
2,
<%#((DataRowView)Container.DataItem)["字段名"] %>
它只能使用在数据源为与数据库有关的Dataset,DatTable,DataView.这些数据源都实现了IListSource接口。其实从低层实现本质上来看,它和<%#((Type)Container.DataItem)。成员 %>相似。
3,
<%#Container.DataItem%> <%#GetDataItem()%> <%#((Type)Container.DataItem).成员 %> <%#((Type)GetDataItem()).成员 %>
这几种形式估计你们最不经常使用。它们通常只使用与普通集合(例如:数组,ArrayList,HashTable)和泛行集合(例如:List<T>,Dictionary<Tkey,Tvalue>)。其实本质上就是实现了IList,ICollection,IEnumerable,IDictionary等以及这些接口对应的泛行接口的集合。IList接口和IDictionary接口的区别是,一个只有值,而另外一个是键/值对,对应泛行形式也是这样。而Array就对用List<T>,而HashTable就对应Dictionary<Tkey,Tvalue>.
转自 http://www.rjgc.net/control/content/content.php?nid=11386
做为.NET平台软件开发者,咱们频繁与各类各样的数据交互,这些数据经常来源于文本、自定义类型、XML、数据库等,访问这些数据有不少方法,而数据绑定表达式即是其中最经常使用也是最实用的方法之一。我用2篇博文,尽可能说透。NET平台数据绑定表达的来源,使用方法,底层原理,效率等。另外这2篇博文我最初发表于博客园。
数据绑定表达式必须包含在<%#和%>字符之间。格式以下:
<tagprefix:tagname property='<%# data-binding expression %>' runat="server" /> |
或者以下:
<%# data-binding expression %> |
ASP.NET 支持分层数据绑定模型,数据绑定表达式使用 Eval 和 Bind 方法将数据绑定到控件,并将更改提交回数据库。
Eval 方法是静态单向(只读)方法,因此Eval 函数用于单向(只读)绑定,该方法采用数据字段的值做为参数并将其做为字符串返回。
Bind 方法支持读/写功能,因此Bind 函数用于双向(可更新)绑定。该方法能够检索数据绑定控件的值并将任何更改提交回数据库。
XPath 方法支持对XML类型的数据源提供支持。
1,能够将数据绑定表达式包含在服务器控件或者普通的html元素的开始标记中属性名/属性值对的值侧。例如:
<asp:TextBox ID="TextBox1" runat="server" Text='<%#数据绑定表达式%>' ></asp:TextBox><br /> |
此时数据的绑顶表达式能够是一个变量,也能够是一个带返回值的c#或者VB.NET方法,还能够是某个控件的某个属性的值,也能够是c#或者VB.NET对象的某个字段或者属性的值等等。固然也能够直接就是一个字符串,例如"hello". 若是此时的数据绑定表达式是Eval("数据库中某个表的某个字段")等,那么必须把TextBox1放在某个循环显示的控件的模板中才正确,不然会提示:Eval()、XPath() 和 Bind() 这类数据绑定方法只能在数据绑定控件的上下文中使用。其实就是想让你把TextBox1放在像Repeater,DataList,GridView这样的控件的模板中。 2,数据绑定绑定表达式包含在在页面中的任何位置。例如:
<form id="form1" runat="server"> <div> <%#Eval("数据绑定表达式1")%> <%#Eval("数据绑定表达式2")%> </div> </form> |
此时数据的绑顶表达式能够是一个变量,也能够是一个带返回值的C#或者VB.NET方法,还能够是某个控件的某个属性的值,也能够是C#或者VB.NET对象的某个字段或者属性的值等等。固然也能够直接就是一个字符串,例如"hello".
若是此时的数据绑定表达式是Eval("数据库中某个表的某个字段")等,那么必须把<%#Eval("数据绑定表达式1")%> <%#Eval("数据绑定表达式2")%> 放在像Repeater,DataList,GridView这样的控件的模板中。
3,能够将数据绑定表达式包含在Javascript代码中,从而实如今Javascript中调用C#或者VB.NET的方法。例如:
Deafult2.aspx: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>无标题页</title> <script language ="javascript" type="text/javascript"> function GetStr() { var a; a = ''; a='<%#CSharpToJavascript()%>' //调用c#的方法 alert(a); } </script> </head> <body> <form id="form1" runat="server"> <div> <input id="Button1" type="button" value="Javascript调用c#的方法!" onclick="GetStr()" /</div> </form> </body> </html> |
Default2.cs: using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class Default2 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Page.DataBind();//方法有返回值的要先绑定,才能实现Javascript调用c#的方法! } public string CSharpToJavascript() { return "Javascript调用c#的方法!"; } } |
1,能够是一个变量
例如
:<asp:Label ID="Label1" runat="server" Text="<%#变量名%>"></asp:Label> |
2,能够是服务器控件的属性值
例如: <asp:Label ID="Label1" runat="server" Text="<%#TextBox2.Text %>"></asp:Label>
3,能够是一个数组等集合对象
例如把一个数组绑定到列表控件,例如ListBox等,或者Repeater,DataList,GridView这样的控件等,此时只须要把属性DataSource='<%# 数组名%>' .
4,能够是一个表达式
例如:Person是一个对象,Name和City是它的2个属性,则数据绑定表达式能够这样写: <%#(Person.Name + " " + Person.City)%>.
5,能够是一个方法
例如:<%#GetUserName()%>.GetUserName()是一个已经定义的C#方法,通常要求有返回值。
6,能够是用Eval,DateBinder.Eval取得的数据表的字段,这个是最多见的了,再也不举例。
注意:若是数据绑定表达式做为属性的值,只要数据绑定表达式中没有出现双引号,那么<%#数据绑定表达式%>的最外层用双引号或者单引号均可以。若是数据绑定表达式中出现双引号,则<%#数据绑定表达式%>的最外层最好要用单引号。
,<%#DataBinder.Eval(Container.DataItem,"字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名","{0:c}") %> |
还有2种不经常使用的:
<%# DataBinder.Eval(Container,"DataItem.字段名")%> <%# DataBinder.Eval(Container,"DataItem.字段名",{0:c})%> |
Container.DataItem至关于数据库中某个表或者视图中的一行记录,而一行能够有不少列。
使用三目运算符?:的例子:
<%# DataBinder.Eval(Container.DataItem, "字段名")。ToString()。Trim()。Length>16?DataBinder.Eval(Container.DataItem, "字段名")。ToString()。Trim()。Substring(0,16): DataBinder.Eval(Container.DataItem, "字段名")。ToString()。Trim() %> |
2,<%#Eval("字段名")%>
<%#Eval("字段名","{0:c}")%>
.NET 2.0新出现的一个方法。和DataBinder.Eval()等价。
绑定生日的例子:
<%#string.Format("{0:yyyy-MM-dd dddd}",Eval("stuBirth"))%> <%# DataBinder.Eval(Container.DataItem,"stuBirth","{0:yyyy-MM-dd}")%> |
使用三目运算符的例子:
<%#(Eval("性别"))。ToString() =="True"?"男":"女"%>
性别字段类型为:是/否(Access),bit(sql server)
调用方法的例子:
<%# GetUserPhoto(Eval("PhotoPath")) %> GetUserPhoto()的定义: string GetUserPhoto(object photoPath) { if (photoPath == DBNull.Value)<%#((DataRowView)Container.DataItem)["字段名"] %> 绑定到DataView,DataTable,DataSet { return "<img src='Images/none.gif'>"; } else { return "<img src='Upload/" +photoPath.ToString() + "'>"; } } |
3, <%#((DataRowView)Container.DataItem)["字段名"] %>
<%# string.Format("{0:c}", ((DataRowView)Container.DataItem)["字段名"])%> |
Container.DataItem至关于数据库中某个表或者视图中的一行记录,而一行能够有不少列。
类型转换后相乘的例子:
<%# (int)((DataRowView)Container.DataItem)["字段名1"]*(int) ((DataRowView)Container.DataItem)["字段名2"]%> |
上面三种绑定方法的效率:Eval方法执行时候会调用DataBinder.Eval方法,DataBinder.Eval方法在运行时使用反射执行后期绑定计算,会致使性能明显降低。因此会致使性能明显降低。因此三者中<%#((DataRowView)Container.DataItem)["字段名"] %>的性能最好。
这一节继续来谈。NET中的数据绑定表达式。
本节涉及的内容以下:
一,数据绑定方法的来源以及在低层上的实现。
二,数据绑定方法的执行效率排序。
<%#Container.DataItem%> <%#GetDataItem()%> <%#Eval("字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名")%> <%#((DataRowView)Container.DataItem)["字段名"] %> <%#((Type)Container.DataItem).成员 %> <%#((Type)GetDataItem()).成员 %>
上面七种绑定形式以及它们的变幻形式都用过吗?性能怎么排序?
复习一下:第一节咱们主要谈了数据绑定表达式的各类形式,在ASP.NET页面中出现的位置,以及咱们常绑定到与数据库有关的DataView,DataTable,DataSet 等数据源的数据绑定表达式的各类形式。
你有没有对Eval方法和DataBinder.Eval方法好奇过?
在.NET2.0中咱们常常用Eval方法在Repeater,DataList,GridView等循环控件中绑定数据,Eval方法和DataBinder.Eval方法在低层是怎么实现的?它们到底有什么千丝万缕的关系?
咱们经常使用的Eval方法实际上是Page类的一个静态单向只读方法,并且它是一个受保护的方法。实际上Page类的Eval方法是继承自TemplateControl类的。TemplateControl 类是一个抽象类,它为Page 类和 UserControl 类提供通用属性和方法。咱们先来看一下继承家谱:
System.Object System.Web.UI.Control System.Web.UI.TemplateControl System.Web.UI.Page System.Web.UI.UserControl
Eval方法就是TemplateControl类的方法,它有两种形式:
名称 |
说明 |
TemplateControl.Eval (String) |
计算数据绑定表达式。 |
TemplateControl.Eval (String, String) |
使用用于显示结果的指定格式字符串计算数据绑定表达式。 |
事实上TemplateControl类还提供了XPath方法和XPathSelect方法供Page类和UserControl继承。这2个方法是和XML数据源有关的绑定方法。
若是细心的你查看TemplateControl类的基类Control类,你就会发现其实Control类并无提供Eval,XPath,XPathSelect等方法。因此Eval,XPath等方法最终是在TemplateControl类中实现的。
如今,终于找到了Eval,XPath等数据绑定方法的来源了。
Eval,XPath等方法是。NET 2.0新增的方法。在。NET 1.1时代咱们常常用的是DateBinder.Eval方法。形如:
<%#DataBind.Eval(Container.DataItem,"字段名") %>
<%#DataBind.Eval(Container.DataItem,"字段名","{0:c}") %>
Eval的出现其实就是为了简化DataBinder.Eval方法的写法从而代替它。
在ASP.NET 2.0中及以上,当咱们调用Eval时,Eval 方法会使用GetDataItem方法调用DataBinder.Eval方法计算表达式的值。要想理解这句话,就算查边MSDN也一头雾水,除非咱们知道Eval方法的源代码,不然根本找不到蛛丝马迹。这里就要用到反射了。咱们经过反射得到了Eval方法的源代码:
protected internal object Eval(string expression)
{
this.CheckPageExists();
return DataBinder.Eval(this.Page.GetDataItem(), expression);
}
终于见到GetDataItem()方法了,其实它就是Page类的一个方法,也是。NET 2.0新增一个方法。GetDataItem()方法的做用是为了得到Container.DataItem,它是。NET 2.0中用来代替Container.DataItem的,若是你曾经用Repeater和DataList等绑定过数组或者ArrayList等,你就会发现<%#GetDataItem()%>和<%#Container.DataItem%>等价。同时,能够确定:Eval方法在低层上确实是调用DataBinder.Eval方法实现数据绑定的。其中“this.CheckPageExists();” 是检查调用的时候有没有Page对象的,若是没有则会抛出一个异常。
要弄清Eval是怎么工做的,GetDataItem()方法的低层实现咱们也要用反射来获取:
public object GetDataItem()
{
if ((this._dataBindingContext == null) || (this._dataBindingContext.Count == 0))
{
throw new InvalidOperationException(SR.GetString("Page_MissingDataBindingContext"));
}
return this._dataBindingContext.Peek();
}
咱们从GatDataItem()方法中看到“return this._dataBindingContext.Peek();”很快就猜测_dataBindingContext是否是一个堆栈呢?事实它就是一个堆栈!经过反射查看源代码咱们得出:_dataBindingContext是一个Stack类型对象。因此它有Peek方法。“return this._dataBindingContext.Peek(); ”正是把堆栈顶部的元素返回。而if语句是用来判断这个堆栈是否已经存在或者是否已经有元素存在,若是if不成立,就会抛出一个异常。
从上面的分析咱们知道:_dataBindingContext堆栈的做用是经过GetDataItem()方法这个桥梁向Eval方法提供Container.DateItem.用逆向思惟来理解上面这句话:Eval方法能够自动计算出Container.DataItem,缘由就是从dataBindingContext堆栈来获取Container.DataItem;这也就为何Eval方法可以知道形如<%#Eval"字段名"%>中字段名隶属于哪一个数据项的属性的缘由;同时咱们也知道。NET 2.0中的Eval在本质上的实现并无抛弃Container.DataItem,而Container.DataItem在2.0时代也没有消失。
那么_dataBindingContext这个保存Container.DataItem的堆栈是怎么创建的呢?
咱们很快就想到每次绑定控件时候最后那条语句是什么:this.控件ID.DataBind();对就是DataBind()方法,DataBind()方法还有一个重载:DataBind(bool raiseOnDataBinding)。为_dataBindingContext这个堆栈压入元素和弹出元素的方法正是用DataBind(bool flag)这个重载方法实现的。
DataBind(bool raiseOnDataBinding)在低层的实现:
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方法 } } }
当咱们执行到this.控件ID.DataBind();时候。在低层上就会调用这个重载的方法来准备包含DataItem的_DatBindingContext堆栈。
上面的代码中提到了DataBinding事件,那么它通常何时被触发呢?
1,若是用编程方式,那么在咱们调用DataBind()方法时候自动触发DataBinding事件。
2,若是咱们用数据源控件(例如SqlDataSource等),当把控件绑定到数据源控件时候,这个事件就会自动触发。
通常数据绑定表达式经常放在模板中循环显示数据,例如Repeater和DataList等的模板。那么下面这个知识点应该知道:Repeater,DataList,FormView等控件必须使用模板,若是不使用模板,这些控件将没法显示数据。而GridView,DetailsView,Menu等控件也支持模板,但显示数据时不是必须的。而TreeView控件不支持模板。
注意:通常状况下,数据绑定表达式不会自动计算它的值,除非它所在的页或者控件显示调用DataBind()方法。DataBind()方法可以将数据源绑定到被调用的服务器控件及其全部子控件,同时分析并计算数据绑定表达式的值。
终于写的有点眉目了,好累!咱们该回头看看Eval方法调用的静态DataBinder.Eval方法在低层的实现了。我把DataBinder类的源代码做为附近提供下载。
从“一”讲述的低层实现。咱们很容易来排序下面数据绑定表达式的执行效率
<%#Container.DataItem%> <%#GetDataItem()%> <%#Eval("字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名")%> <%#((DataRowView)Container.DataItem)["字段名"] %> <%#((Type)Container.DataItem).成员 %> <%#((Type)GetDataItem()).成员 %>
1,效率最高应该是:
<%#((Type)Container.DataItem).成员 %> <%#Container.DataItem%> <%#((DataRowView)Container.DataItem)["字段名"] %>
2,效率排第二的是:
<%#((Type)GetDataItem()).成员 %> <%#GetDataItem()%>
3,效率最低的是:
<%#Eval("字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名")%>
其实按上面的排序有失公允,缘由是这七种数据表达绑定形式运用的场合不是彻底相同的。
使用场合大概以下:
1, |<%#Eval("字段名")%> <%#DataBinder.Eval(Container.DataItem,"字段名")%>
它们的使用场合最广,数据源能够为与数据库有关的DataSet,DataTable,DataView.也能够为普通集合(例如:数组,ArrayList,HashTable等)和泛行集合(例如:List<T>,Dictionary<Tkey,Tvalue>等)。
注:它们2个永远能够相互替换,至少目前是这样,凡是能够用Eval方法的地方,就能够用DataBinder.Eval方法替换。从低层实现上,Eval比DataBinder.Eval方法效率稍低,缘由是Eval方法对了调用GetDataItem()方法这一步。但最终都是经过DataBinder.Eval方法利用反射技术根据名称查找属性,从而计算出表达式的值,因此很是影响性能。
2,
<%#((DataRowView)Container.DataItem)["字段名"] %>
它只能使用在数据源为与数据库有关的Dataset,DatTable,DataView.这些数据源都实现了IListSource接口。其实从低层实现本质上来看,它和<%#((Type)Container.DataItem)。成员 %>相似。
3,
<%#Container.DataItem%> <%#GetDataItem()%> <%#((Type)Container.DataItem).成员 %> <%#((Type)GetDataItem()).成员 %>
这几种形式估计你们最不经常使用。它们通常只使用与普通集合(例如:数组,ArrayList,HashTable)和泛行集合(例如:List<T>,Dictionary<Tkey,Tvalue>)。其实本质上就是实现了IList,ICollection,IEnumerable,IDictionary等以及这些接口对应的泛行接口的集合。IList接口和IDictionary接口的区别是,一个只有值,而另外一个是键/值对,对应泛行形式也是这样。而Array就对用List<T>,而HashTable就对应Dictionary<Tkey,Tvalue>.
转自 http://www.rjgc.net/control/content/content.php?nid=11386