在.aspx的文件中经常会碰到如下的代码,如:
1、<%=%> 里面放的变量名,如:
Welcome to Beginning ASP.NET 3.5 on <%=
DateTime.Now.ToString() %>
Welcome to Beginning ASP.NET 3.5 on 2009-11-10 15:53:08
2、 <%#%> 这里是数据的绑定
如:<%# DataBinder.(Container.DataItem, "ClassName") %>
完整代码:
3、<%@ %> 表示:引用
如在很多.aspx页面中,都可以看到如下的代码:
<%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="_Default" %>
4、<%%>中间一般放函数或者方法,典型的asp程序写法。
例如:
Container.DataItem
Eval
Bind
DataBinder.Eval
eval()方法在运行时使用反射执行后期绑定计算,因此与标准的ASP.NET数据绑定方法bind相比,会导致性能明显下降。它一般用在绑定时需要格式化字符串的情况下。多数情况尽量少用此方法
Eval 方法是静态(只读)方法,该方法采用数据字段的值作为参数并将其作为字符串返回。Bind 方法支持读/写功能,可以检索数据绑定控件的值并将任何更改提交回数据库。
使用 Eval 方法
Eval 方法可计算数据绑定控件(如 GridView、DetailsView 和 FormView 控件)的模板中的后期绑定数据表达式。在运行时,Eval 方法调用 DataBinder 对象的 Eval 方法,同时引用命名容器的当前数据项。命名容器通常是包含完整记录的数据绑定控件的最小组成部分,如 GridView 控件中的一行。因此,只能对数据绑定控件的模板内的绑定使用 Eval 方法。
Eval 方法以数据字段的名称作为参数,从数据源的当前记录返回一个包含该字段值的字符串。可以提供第二个参数来指定返回字符串的格式,该参数为可选参数。字符串格式参数使用为 String 类的 Format 方法定义的语法。
使用 Bind 方法
Bind 方法与 Eval 方法有一些相似之处,但也存在很大的差异。虽然可以像使用 Eval 方法一样使用 Bind 方法来检索数据绑定字段的值,但当数据可以被修改时,还是要使用 Bind 方法。
在 ASP.NET 中,数据绑定控件(如 GridView、DetailsView 和 FormView 控件)可自动使用数据源控件的更新、删除和插入操作。例如,如果已为数据源控件定义了 SQL Select、Insert、Delete 和 Update 语句,则通过使用 GridView、DetailsView 或 FormView 控件模板中的 Bind 方法,就可以使控件从模板中的子控件中提取值,并将这些值传递给数据源控件。然后数据源控件将执行适当的数据库命令。出于这个原因,在数据绑定控件的 EditItemTemplate 或 InsertItemTemplate 中要使用 Bind 函数。
Bind 方法通常与输入控件一起使用,例如由编辑模式中的 GridView 行所呈现的 TextBox 控件。当数据绑定控件将这些输入控件作为自身呈现的一部分创建时,该方法便可提取输入值。
Bind 方法采用数据字段的名称作为参数,从而与绑定属性关联,如下面的示例所示:
Employee ID:
<%# Eval("EmployeeID") %>
First Name:
Last Name:
单击行的 Update 按钮时,使用 Bind 语法绑定的每个控件属性值都会被提取出来,并传递给数据源控件以执行更新操作。
使用 DataBinder.Eval
ASP.NET 提供了一个名为 DataBinder.Eval 的静态方法,该方法计算后期绑定的数据绑定表达式,并将结果格式化为字符串(可选)。利用此方法,可以避免许多在将值强制为所需数据类型时必须执行的显式强制转换操作。
例如,在下面的代码片段中,一个整数显示为货币字符串。使用标准的 ASP.NET 数据绑定语法,必须首先强制转换数据行的类型以便检索数据字段 IntegerValue。然后,这将作为参数传递到 String.Format 方法:
<%# String.Format("{0:c}", ((DataRowView)Container.DataItem)["IntegerValue"]) %>
将此语法与 DataBinder.Eval 的语法进行比较,后者只有三个参数:数据项的命名容器、数据字段名称和格式字符串。在模板化列表中(如 DataList 类、DataGrid 类或 Repeater 类),命名容器始终是 Container.DataItem。
<%# DataBinder.Eval(Container.DataItem, "IntegerValue", "{0:c}") %>
格式字符串参数是可选的。如果它被忽略,DataBinder.Eval 将返回类型对象的值,如下面的示例所示:
<%# (bool)DataBinder.Eval(Container.DataItem, "BoolValue") %>
当对模板化列表中的控件进行数据绑定时,DataBinder.Eval 特别有用,因为数据行和数据字段通常都必须强制转换。
ASP.NET 2.0改善了模板中的数据绑定操作,把v1.x中的数据绑定语法DataBinder.Eval(Container.DataItem, fieldname)简化为Eval(fieldname)。Eval方法与DataBinder.Eval一样可以接受一个可选的格式化字符串参数。缩短的Eval语法与DataBinder.Eval的不同点在于,Eval会根据最近的容器对象(例如DataListItem)的DataItem属性来自动地解析字段,而DataBinder.Eval需要使用参数来指定容器。由于这个原因,Eval只能在数据绑定控件的模板中使用,而不能用于 Page(页面)层。当然,ASP.NET 2.0页面中仍然支持DataBinder.Eval,你可以在不支持简化的Eval语法的环境中使用它。
Asp.net中DataBinder.Eval用法的总结
<%# Bind("Subject") %> //绑定字段
<%# Container.DataItemIndex + 1%> //实现自动编号
<%# DataBinder.Eval(Container.DataItem, "[n]") %>
通常使用的方法(这三个性能最好)
<%# DataBinder.Eval(Container.DataItem, "ColumnName") %>
<%# DataBinder.Eval(Container.DataItem, "ColumnName", null) %>
<%# DataBinder.Eval(Container, "DataItem.ColumnName", null) %>
其他用法
<%# ((DataRowView)Container.DataItem)["ColumnName"] %>
<%# ((DataRowView)Container.DataItem).Row["ColumnName"] %>
<%# ((DataRowView)Container.DataItem)["adtitle"] %>
<%# ((DataRowView)Container.DataItem)[n] %>
<%# ((DbDataRecord)Container.DataItem)[0] %>
<%# (((自定义类型)Container.DataItem)).属性.ToString() %>//如果属性为字符串类型就不用ToString()了
DataBinder.Eval用法范例
<%# DataBinder.Eval(Container.DataItem, "IntegerValue", "{0:c}") %>
格式化字符串参数是可选的。如果忽略参数,DataBinder.Eval 返回对象类型的值,
//显示二位小数
<%# DataBinder.Eval(Container.DataItem, "UnitPrice", "${0:F2}") %>
//{0:G}代表显示True或False
<ItemTemplate>
<asp:Image Width="12" Height="12" Border="0" runat="server"
AlternateText='<%# DataBinder.Eval(Container.DataItem, "Discontinued", "{0:G}") %>'
ImageUrl='<%# DataBinder.Eval(Container.DataItem, "Discontinued", "~/images/{0:G}.gif") %>' />
ItemTemplate>
//转换类型
((string)DataBinder.Eval(Container, "DataItem.P_SHIP_TIME_SBM8")).Substring(4,4)
{0:d} 日期只显示年月日
{0:yyyy-mm-dd} 按格式显示年月日
{0:c} 货币样式
<%#Container.DataItem("price","{0:¥#,##0.00}")%>
<%# DataBinder.Eval(Container.DataItem,"Company_Ureg_Date","{0:yyyy-M-d}")%>
Specifier Type Format Output (Passed Double 1.42) Output (Passed Int -12400)
c Currency {0:c} $1.42 -$12,400
d Decimal {0:d} System.FormatException -12400
e Scientific {0:e} 1.420000e+000 -1.240000e+004
f Fixed point {0:f} 1.42 -12400.00
g General {0:g} 1.42 -12400
n Number with commas for thousands {0:n} 1.42 -12,400
r Round trippable {0:r} 1.42 System.FormatException
x Hexadecimal {0:x4} System.FormatException cf90
{0:d} 日期只显示年月日
{0:yyyy-mm-dd} 按格式显示年月日
样式取决于 Web.config 中的设置
{0:c} 或 {0:£0,000.00} 货币样式 标准英国货币样式
<system.web>
<globalization requestEncoding="utf-8" responseEncoding="utf-8" culture="en-US" uiCulture="en-US" />
system.web>
显示为 £3,000.10
{0:c} 或 string.Format("{0:C}", price); 中国货币样式
<system.web>
<globalization requestEncoding="utf-8" responseEncoding="utf-8" culture="zh-cn" uiCulture="zh-cn" />
system.web>
显示为 ¥3,000.10
{0:c} 或 string.Format("{0:C}", price); 美国货币样式
<system.web>
<globalization requestEncoding="utf-8" responseEncoding="utf-8" />
system.web>
显示为 $3,000.10
经常会碰到在前台代码中要使用(或绑定)后台代码中变量值的问题。一般有<%= str%>和<%# str %>两种方式,这里简单总结一下。如有错误或异议之处,敬请各位指教。
一方面,这里所讲的前台即通常的.aspx文件,后台指的是与aspx相关联的CodeBehind,文件后缀名为.aspx.cs;另一方面,这里的绑定是指用户发出访问某一页面指令后,服务器端在执行过程中就已经将前台代码进行了赋值,而后生成html格式回传客户端显示,而并非已经显示到客户端后,然后通过其他方法(如ajax)去服务器端获取相应变量。
备注:上面说的两个文件是常见的代码隐藏(code-behind)模式,还有一种是代码嵌入(code-beside, inline)模式,那就是只存在aspx一个文件,而后台代码则写入此文件的之中(还有一些语法上区别),这对于本文讨论的问题略有影响,因为代码嵌入是声明性代码与C#/VB.NET代码都一起编译到一个类里面,而代码隐藏则将声明性代码与C#/VB.NET代码分开几次进行翻译/编译,因此前者是局部与局部(partial)的关系后者基类与派生类的关系,但这仅仅影响所能绑定变量的范围(与修饰符有关),下面会提到。以下均以代码隐藏模式为例。
一般来说,在前台代码的三种位置可能会用到(绑定)后台变量:
对于第一种位置,有一些约束条件:
(1)一般的属性要求是字符串型或数值型(下面会提到有些服务器端属性支持属性为数据集合);
(2)并不是所有的属性都可以绑定变量,有些属性例如runat属性必须是"server"常量,即使绑定的字符串是server,也会导致分析器分析时出错;
(3)有一种属性,他要求属性值有约束(类型约束,比如服务器端控件要求TabIndex属性是short类型,或者字符串内容有约束),也应该在绑定时满足,否则依然可能编译时报错;
(4)还一种属性,虽然属性本身有约束,但即使绑定的变量不满足约束,也可以编译通过,比如input的checked属性,它只有checked字符串是合法的,但如果通过绑定获取到的字符串不是checked,那么这些属性将有自己内部处理机制,来保证可以正常使用;
(5)还要注意,即使对于同一类属性,服务器端和HTML的属性的处理机制也不同,同样是TabIndex(tabIndex),前者如果不满足,则分析器错误,后者则忽略这一问题。
对于第二种位置,一般只要绑定的后台变量和JavaScript中数据类型兼容即可。
对于第三种位置,如果绑定出现的位置不在服务器端控件内部,则没有约束条件,只要是常量字符串可以出现的位置,均可以绑定。但是对于置于服务器端控件内部,也就是上面那种
(1)约束型控件:这类控件要求它的开始标签和结束标签中只能包含指定的子控件,因此如果在这里出现代码块,将编译错误。例如:
(2)非嵌套类控件:这类控件,不允许在内部嵌套其他控件或标签,只能是常量字符串,它会将开始标签和结束标签中常量字符串内容作为他的属性。例如上面提到的TextBox,它会将标签间内容作为它的Text属性值。
(3)嵌套类控件:这类控件,可以嵌套其他任意控件,也可以包含字符串,因此可以正常显示绑定代码块所表示的字符串内容。例如Label控件、Panel等。
(4)数据绑定类控件:这类控件是ASP.NET提供的服务器端控件,除了可以绑定普通的变量类型,也可以绑定一个数据集合(只能采取下面的第二种方式实现)。
关于是否加引号:在以上三个位置使用时,是否应该将<%= str%>或<%# str %>置于单引号或双引号中呢?对于在不同位置,处理的方式是不同的:(具体请在下面两种方式的具体介绍时,加以体会)
(1)对于第一种位置,由于JavaScript是弱类型的,如果绑定时加引号,显然就认为就当做字符串来处理,这始终是正确的;如果绑定时不加引号,它将认为这是个数值型的,那么如果获取的真是数值,当然可以,如果是非数值型,则将产生脚本错误,这即使对于JavaScript赋值常量时,也是同样的:
var test1 = 123b;//运行时报错 var test2=123;//正确,是数值型 var test3="123b";//正确,字符串型
(2)对于第二种位置,经过测试,无论是对于服务器端控件属性还是HTML标签属性,加引号总是正确的;如果不加引号,则两种属性的处理方式不同:
(3)对于第三种位置,加与不加引号,获取的值及其显示均不受影响。
因此建议,所有绑定表达式都加上引号,作为字符串获取,然后根据实际需求,用相应函数进行转换,得到所需要的类型。
另外,这里所说的后台变量是泛指的,包括如下:
数据集合
后台变量有一些约束条件,需要满足:
(1)变量修饰符要求。变量是静态或者实例字段均可。对于代码隐藏模式的ASP.NET,以上的所述的变量必须为public或protected类型(因为是基类与派生类的关系),private或者internal都不行,而代码嵌入模式则任何修饰符的变量均可访问(一个类内部的关系)。
(2)变量类型要求。由于前台属性一般是字符串类型,而JavaScript基本类型也就是字符串型、数字型、布尔型,因此对应的变量应该也是这几种方式,其余类型如果不被支持(如复杂类型、数组、引用类型等),前台获取的就是调用了变量的ToString()方法所得到的字符串。因此,在绑定时,要根据情况看是否能进行隐式类型转换,必要时还要用相关函数来强制转换,以保证前台可以获得正确的值。当然,对于数据绑定类控件,它的有些属性可以为数据集合,但这时的绑定只能通过下面第二种方式才被支持。
以上是一些概念和基本约束,这些都是两种方式都应该满足的,下面具体介绍两种方式,来实现前台代码中(以下称为代码块)绑定后台变量的功能。
一. <%= str%>
此种方式其实是ASP 时代就支持的,ASP 通过包含在 < % 和 %>中的表达式将执行结果输出到客户浏览器 , 如:< % =test %>就是将变量test的值发送到客户浏览器中。在ASP.NET中,这个表达式依然可以使用,并可以出现在前台代码的上述三个位置,但是要注意,除了上述的一般性约束外,对于控件属性,还必须是绑定到非服务器端控件的属性。另外,它只能绑定上面讲的前三种变量类型,不支持绑定数据集合。例子如下:
后台代码:
public partial class WebForm2 : System.Web.UI.Page { public string GetVariableStr;//注意变量的修饰符 protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { GetVariableStr = "hello world from variable"; } } protected string GetFunctionStr()//注意返回值的修饰符 { return "hello world from Function"; } }
前台代码:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>title> <script type="text/javascript"> function fun() { var str = '<%= DateTime.Now %>'; //前台位置1,绑定的是第三种变量类型(也是第二种方式,?因为Now是个属性) alert(str); } script> head> <body onload="fun()"> <form id="form1" runat="server"> <div> <input type="text" value="<%= GetVariableStr %>" /> <%--前台位置2,绑定的是成员变量--%> "<%= GetFunctionStr() %>" <%--前台位置3,绑定的是一个方法的返回值>--%> div> form> body> html>
一些错误的使用:
之所以说第一种绑定方式要用于非服务器端控件的属性,是因为如果应用于这些服务器端属性时,这些代码实际上不被解析。 比如:
<asp:Label ID="Label1" runat="server" Text="<%= GetVariableStr %>">asp:Label> <asp:TextBox ID="TextBox1" runat="server" Text="<%= GetVariableStr %>">asp:TextBox>
则显示出来的Label1的文本是空,而TextBox中文本是"<%= GetVariableStr %>”,所以记住,对服务器端控件的属性加这样的代码块,将不被解析,而是将这一字符串直接作为属性值了,所以不是想要的结果。如果引号也不加上,将会编译错误,提示“服务器标记不能包含 <% ... %> 构造。”。
这里结合开篇提到的关于将绑定代码快置于“Html显示内容的位置”时,如果在服务器端控件内,那四类控件如何显示的问题。如下:
<asp:Label ID="Label1" runat="server" >"<%= GetVariableStr %>"asp:Label> <asp:TextBox ID="TextBox1" runat="server" >"<%= GetVariableStr %>"asp:TextBox>
其中,Label1属于嵌套类控件,Label1确实显示了正确的结果,TextBox属于非嵌套类控件,TextBox如果用这种方式,将会产生编译错误,提示“此上下文中不支持代码块。”
二. <%# str %>
ASP.NET 引入了一种新的声明语法 <%# %>。该语法是在 .aspx 页中使用数据绑定的基础,所有数据绑定表达式都必须包含在这些字符中。这里从用法和适用范围等方面与第一种绑定方式进行区分。
从出现的位置来看,除了能出现在第一种代码块出现的所有位置外,他还可以出现在服务器端控件的属性中。
从绑定的变量类型上看,他还可以配合ASP.NET的数据绑定类控件,来绑定上述的第四种“变量”类型,就是数据集合(DropDownList,DataList,DataGrid,ListBox这些是数据绑定类控件,数据集合包括ArrayList(数组),Hashtable(哈稀表,DataView(数据视图),DataReader等)。
从用法上看,在前台代码中除了在相应位置写上<%# %>外,在后台代码中,还需要使用DataBind()方法。以下是实例:
前台代码:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>title> <script type="text/javascript"> function fun() { var str = '<%# DateTime.Now %>'; alert(str); } script> head> <body onload="fun()"> <form id="form1" runat="server"> <div> <input type="text" value="<%# GetVariableStr %>" /><br /> "<%# GetVariableStr %>" <asp:Label ID="Label1" runat="server" Text="<%# GetVariableStr %>">asp:Label> <%--此种方式可以绑定服务器端控件的属性--%> <asp:DropDownList ID="DropDownList1" runat="server" DataSource='<%# arraylist %>'> <%-- 将集合绑定到数据绑定类控件,通过DataSource属性来实现,从而在下拉框看到集合中的内容--%> asp:DropDownList> <asp:DataList ID="DataList1" runat="server" DataSource='<%# dt %>'> <%-- 同上,绑定了DataTable数据集合?--%> <ItemTemplate> <table border="1" cellpadding="0" cellspacing="0"> <tr> <td> <asp:Label ID="Label2" runat="server" Text='<%# Bind("row0")%>'>asp:Label> <%--由于绑定的数据集合具有多列,并且此数据绑定类控件支持模板, 因此需要在模板中指定需要绑定的列以及格式--%> td> <td> <%# Eval("row1")%> td> tr> table> ItemTemplate> asp:DataList> div> form> body> html>
可以看出,这种方式在使用时,不但可以实现(取代)<%=... %>所满足的功能,还可以绑定服务器控件属性(如上面的Label1),也可以将集合类型绑定到支持的数据绑定类控件。在用法上,前台代码除了对数据绑定类控件绑定数据集合外有所差别,其他的使用上与第一种没区别。在绑定类控件的模板中,如何使用Eval、Bind、DataBinder.Eval等,不在此文讨论中,可以参考下面链接的参考文章。
后台代码:
public partial class WebForm2 : System.Web.UI.Page { public string GetVariableStr; public ArrayList arraylist; public DataTable dt; protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { GetVariableStr = "hello world from variable"; arraylist = new ArrayList(); arraylist.Add("选?项?1"); arraylist.Add("选?项?2"); dt = new DataTable(); dt.Columns.Add("row0"); dt.Columns.Add("row1"); DataRow dr1 = dt.NewRow(); dr1[0] = "1.1"; dr1[1] = "1.2"; DataRow dr2 = dt.NewRow(); dr2[0] = "2.1"; dr2[1] = "2.2"; dt.Rows.Add(dr1); dt.Rows.Add(dr2); Page.DataBind(); //DropDownList1.DataBind(); //DataList1.DataBind(); } } }
在后台代码中,与第一种方式唯一不同的,就是需要调用DataBind方法。只有执行了相应控件的DataBind方法,前台代码中这些控件中使用<%# %>的绑定才会发生(并且控件内部的所有绑定也会发生,比如又嵌套了一个绑定后台数据的控件),否则得话将不会被赋值,而是默认空值。上面我们用的是Page的DataBind方法,那么整个页面所有绑定都会执行。当然,如果我们只执行DataList1或者DropDownList1的DataBind方法,那么只有相应控件的绑定才会发生。需要注意的是,这里说的需要执行DataBind包括了显示和隐式执行,有些数据绑定类控件,当它们通过 DataSourceID 属性绑定到数据源控件时,会通过隐式调用 DataBind 方法来执行绑定。这时就不必显示的再次调用了。
两者区别:
两种绑定方式上,他们的约束基本相同,都要求与属性匹配,出现在他们可以出现的位置。后者的使用位置更广泛,尤其是支持服务器端控件和绑定数据集合。后台代码方面,后者需要调用DataBind才能完成绑定,前者则没有这方面要求。这里主要区别一下两者在执行机制上的区别:<%=...%>是在程序执行时调用(应该是在页面的RenderControl事件过程中完成的,也就是通常情况下我们可以看到的后台代码都执行完毕后再去到前台代码中进行赋值绑定),而<%#... %>是在DataBind()方法之后被调用,一旦调用了DataBind(),则它对应的控件将绑定变量,因此,请注意:如果在DataBind()后再修改变量,那么绑定的就不是最新值了,这就需要在完成变量的赋值后,再去DataBind()。其实这两种方式,它的运行过程可以在VS中通过设置断点来看看,看两者的绑定赋值分别是在什么时候发生的。
尚存的疑问:
1.不知道为什么不能获取到internal修饰的变量?
答:此问题已经在下一篇文章《ASP.NET前台无法访问后台internal类型变量的问题》中分析并解决。(2010-10-25日更新)
补充1:回答关于9楼的提到的ASP.NET 4.0中的<%: xxxx%>(2010-11-4)
答:这种方式是ASP.NET4.0中新加入的绑定方式,常用于MVC中,但普通webform中也可使用。他的功能其实就是对绑定的值进行一下编码,因此,一下两者是等同的:
<%= Server.HtmlEncode("test") %>
<%: "test" %>;
补充2:关于各种百分号的功能区分(2010-11-4)
答:本文上面讲了两种百分号的用法,在加上补充1中的,一共讲了三种,其实还有一种百分号用法就是<% func(xx)%>,也就是百分号后不带任何符号,然后执行一段代码,一句话概括:
在百分号內 , 如果百分号后面不带任何符号(冒号、等号、井号) , 即表示要执行一段代码而已,此处不包含任何输出信息;若带符号,即表示执行此处的代码,并且将执行后返回的值绑定(或者显示)在此处。
参考文章:
ASP.NET 数据绑定概述
asp.net代码中尖括号和百分号的含义
Page.DataBind()方法
Eval( " ")和DataBinder.Eval(Container.DataItem, " ")的区别及用法
体会Bind和Eval的不同用法
深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)
<%# Eval("name")%>与<%# Bind("name")%>区别
〈%# 〉与〈%= 〉的区别,显示数据与绑定数据(未参考本文,但后来发现讲的也比较好)