在ASP.NET我们在使用Repeater,DetailsView,FormView,GridView等数据绑定模板时,都会使用<%# Eval("字段名") %>或<%# Bind("字段名") %>这样的语法来单向或双向绑定数据。但是我们却很少去了解,在这些语法的背后,ASP.NET究竟都做了哪些事情来方便我们使用这样的语法来绑定数据。究竟解析这样的语法是在编译时,还是运行时?如果没有深入去了解,我们肯定不得而知。这个简短的系列文章就是带我们大家一起去深入探究一下ASP.NET绑定语法的内部机理,以让我们更加全面的认识和运用它。
事件的起因是,我希望动态的为Repeater控件添加行项模板,我可以通过实现ITempate接口的方式来动态添加行模板。并希望它通过普通的页面绑定语法来完成数据字段的绑定功能,如下就是一个简单的例子:
1: /// <summary>
2: /// Summary description for DynamicTemplate
3: /// </summary>
4: public class DynamicTemplate : ITemplate
5: {
6: public DynamicTemplate()
7: {
8: //
9: // TODO: Add constructor logic here
10: //
11: }
12: #region ITemplate Members
13:
14: public void InstantiateIn(Control container)
15: {
16: TextBox textBox = new TextBox();
17: textBox.Text = @"<%# Eval(""ID"") %>";
18: container.Controls.Add(textBox);
19: }
20: #endregion
21: }
在这个例子中,我在模板中添加了一个TextBox控件,并指定它的绑定字段是“ID”。但是这做法,能否实现我们实现我们需要的功能呢?答案是否定,每一行的TextBox的值都是"<%# Eval(""ID"") %>",而不会像我们希望的那样去绑定ID字段。从结果来分析原因,我们可以非常容易得出,这段绑定语法并没有得到ASP.NET运行时的承认,那么页面中使用相同的语法为什么可以呢?故事就是从这里开始的。
我们首先要去了解下,在页面中使用这样的语法ASP.NET都为我们做了哪些事情呢?要了解这个,我们要找到.aspx文件在首次运行时动态编译的程序集。
我们都知道,在ASP.NET运行时,也会把.aspx文件编译成一个动态类,这个类是继承于.aspx的Page指令中Inherits属性指定的类并且同时也直接实现了IHttpHandler接口。这个动态类会负责创建页面中使用的各种服务器端控件的实例,并且ASP.NET运行时会负责解析的编译.aspx中存在的服务器端代码(包括绑定语法)并将这些代码编译到这个页面类。WebSite工程和Web Application在页面文件上有些不同,WebSite工程的每个页面最多可以有两个文件:.aspx和.aspx.cs文件;而在Web Application还可以包括.aspx.designer.cs文件,这个文件所起的作用也非常有限,也就是为了能在页面代码中使用服务器端、控件实例而定义的一个实例变量,仅此而已。所以在设计时WebSite具备更多的动态行为,而在运行时WebSite工程和Web Application并没有太大区别。
如何得到页面的动态类呢?要首先得到这个页所在的动态程序集,在Vista以前的操作系统上,一般是在:%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files 文件夹下,而在Vista中,而会在:%USERPROFILE%\AppData\Local\Temp\Temporary ASP.NET Files下。那么如何快速得到程序集的路径和名称?你可以让你的Web工程动态编译出错(比如重复的类名),就可以快速定位到当前动态程序集的目录了。
动态类中会有很多的内容,我们不作更多的分析,我们把目光集中绑定代码上。假设现在页面上有这么一段Repeater绑定代码:
1: <asp:Repeater runat="server" ID="repeater">
2: <HeaderTemplate>
3: <table>
4: <tr>
5: <td>
6: ID
7: </td>
8: <td>
9: 电流{a}
10: </td>
11: <td>电压(V)</td>
12: <td>
13: 备注'")%>名称]")%>
14: </td>
15: <td>
16: 名称]
17: </td>
18: </tr>
19: </HeaderTemplate>
20: <ItemTemplate>
21: <tr>
22: <td>
23: <%# Eval("ID")%>
24: </td>
25: <td>
26: <%# Eval("电流{a}")%>
27: </td>
28: <td><%# Eval("电压(V)")%></td>
29: <td>
30: <%# Eval("备注'
31: </td>
32: <td>
33: <%# Eval("
34: </td>
35: </tr>
36: </ItemTemplate>
37: <FooterTemplate>
38: </table>
39: </FooterTemplate>
40: </asp:Repeater>
那么在动态类中,相应的会有这样的一段函数,是用来创建ID为repeater的控件实例:
1: [DebuggerNonUserCode]
2: private Repeater __BuildControlrepeater()
3: {
4: Repeater repeater = new Repeater();
5: base.repeater = repeater;
6: repeater.HeaderTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control4));
7: repeater.ItemTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control5));
8: repeater.FooterTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control7));
9: repeater.ID = "repeater";
10: return repeater;
11: }
12:
13:
CompiledTempateBuilder和BuildTemplateMethod只是模板实例化的一个中介,真正用于添加模板内容的是后面的那些私有函数,如ItemTempate的模板内容实例的创建就在__BuildControl__control5函数中,这个函数原型定义是:
1: [DebuggerNonUserCode]
2: private void __BuildControl__control5(Control __ctrl)
3: {
4: DataBoundLiteralControl control = this.__BuildControl__control6();
5: IParserAccessor accessor = __ctrl;
6: accessor.AddParsedSubObject(control);
7: }
8:
在这个函数里,调用了另一个私有函数this.__BuildControl__control6,这个函数返回的一个DataBoundLiteralControl对象,并将对象输出添加到__ctrl参数。事实上,只要我们去阅读CompiledTempateBuilder就发现在,这里的__ctrol对象就是我们在实例化模板时传入的对象,也就是ITemplate中的InstantiateIn方法的那个container参数对象。
为什么使用的是AddParsedSubObject方法,使用这个方法添加子控件相当于告诉父控件,这是一个已经解析好的子控件对象,不需再去将控件解析成HTML代码,而在输出时直接输出Text属性的值即可。从这里我们还可以得知DataBoundLiteralControl的对象,事实上就是承担了字符串拼接的职责,这一点我们可以在后面的分析中得以验证。
__BuildControl__control6私有函数的定义如下:
1: [DebuggerNonUserCode]
2: private DataBoundLiteralControl __BuildControl__control6()
3: {
4: DataBoundLiteralControl control = new DataBoundLiteralControl(5, 4);
5: control.TemplateControl = this;
6: control.SetStaticString(0, "\r\n <tr>\r\n <td>\r\n ");
7: control.SetStaticString(1, "\r\n </td>\r\n <td>\r\n ");
8: control.SetStaticString(2, "\r\n </td>\r\n \r\n <td>\r\n ");
9: control.SetStaticString(3, "\r\n </td>\r\n <td>\r\n ");
10: control.SetStaticString(4, "\r\n </td>\r\n </tr>\r\n ");
11: control.DataBinding += new EventHandler(this.__DataBind__control6);
12: return control;
13: }
在这个函数里面,创建了一个DataBoundLiteralControl对象,并将页面上定义的模板的静态HTML代码添加到该的静态字符串数组里,并且设置了它的绑定事件代理函数__DataBind__control6,该函数的定义:
1: public void __DataBind__control6(object sender, EventArgs e)
2: {
3: DataBoundLiteralControl control = (DataBoundLiteralControl) sender;
4: RepeaterItem bindingContainer = (RepeaterItem) control.BindingContainer;
5: control.SetDataBoundString(0, Convert.ToString(base.Eval("ID"), CultureInfo.CurrentCulture));
6: control.SetDataBoundString(1, Convert.ToString(base.Eval("电流{a}"), CultureInfo.CurrentCulture));
7: control.SetDataBoundString(2, Convert.ToString(base.Eval("备注'"), CultureInfo.CurrentCulture));
8: control.SetDataBoundString(3, Convert.ToString(base.Eval("名称]"), CultureInfo.CurrentCulture));
9: }
在这个函数中,我们看到了真正的数据绑定代码了,它调用了TemplateControl的Eval方法来将当前数据项的相应字段的值取出,并按一定的格式转化后添加到DataBoundLitreralControl对象中,并在DataBoundLiteralControl将StaticString和DataBoundString字符串数组按一定的顺序拼接起来,作为Text属性的输出值。而容器控件则直接向客户端输这段HTML。
下面,我们还有必要来分析下TemplateControl中的Eval方法,这个方法有两种重载,简单起见,我们来分析较为简单的重载:
1: protected internal object Eval(string expression)
2: {
3: this.CheckPageExists();
4: return DataBinder.Eval(this.Page.GetDataItem(), expression);
5: }
这个方法,使用了DataBinder.Eval静态方法来得到绑定表达式(字段名)的值,它的数据是通过this.Page.GetDataItem()这样的一个方法得到的。那么为什么this.Page.GetDataItem()就可以得到当前正在被绑定的数据项呢?原来,在页面绑定数据时,它会有一个堆栈来保存它所有的绑定控件绑定时用到的数据项,我们只需要取得堆栈顶部的那个元素,就可以在页面的作用域内的任何一个位置得到当前正在被绑定的数据项。如上的例子,我们就可以取得当前绑定的RepeaterItem的DataItem的数据项,因此我们不需要与RepeaterItem有任何的联系。
如果硬要用上面的代码来描述数据绑定的全过程,跨度过大。但是有了以上的分析,我们再用文字的形式再来总结下,应该就会一个比较完整的印象了:在ASP.NET的数据模板控件中,可以使用<%# %>这样的语法来将字段值作为一个占位符,用在HTML代码中,可以方便我们设计和生成最终的HTML代码,不需要很多的字符拼接工作。而ASP.NET运行时在首次执行页面时,会为页面编译一个动态类,在这个动态类中会实例化所有的服务器端控件,编译和解析绑据模板控件的绑定语法,并用一些对象和操作来完成数据绑定的字符串接拼接行为。因此绑定语法的解析事实上是编译时的行为,只不过这个编译时是延迟到页面的首次执行时。这就可以解释为什么在我们想在动态添加模板中使用<%# %>这样的绑定语法时,无法解析的原因。
而对于DataBinder.Eval方法,这是ASP.NET提供的一个数据绑定辅助方法。通过这个方法,我们可以方便的从种不同的数据项,如自定义对象或DataRow取出对象的字段(属性值)。从而为我们屏蔽很多不必要的数据来源类型的判断。同时DataBinder这个类还提供了其它的绑定辅助方法,大家可以从MSDN查看更多有用的帮助。
对数据绑定语法的分析,就先到此为一个段落。在上面,我们主要讨论了Eval的单向数据绑定,在接下来的一篇文章中我们会来探讨ASP.NET通过Bind函数(关键字)
来实现数据双向绑定的机理。
发表评论
-
ASP.NET Process Model之一:IIS 和 ASP.NET ISAPI
2010-01-07 22:04 1029前几天有一个朋友在MSN上问我“ASP.NET 从最初的接收到 ... -
深入ASP.NET数据绑定(下)——多样的绑定方式
2010-01-06 19:58 1013在这个系列的上篇中介 ... -
深入ASP.NET数据绑定(中)——数据双向绑定机理
2010-01-06 19:57 1162在上一篇《深入ASP.NET数据绑定(上)》中,我们分析了在. ... -
C#与数据结构--二叉树的遍历
2009-07-09 10:30 22116.2.2 二叉树的存储结构 二叉树的存储可分为两种:顺序 ... -
前序遍历二叉树,中序遍历二叉树,后序遍历二叉树 c#实现
2009-07-08 23:31 2231using System.Collections.Generi ... -
如何自己实现IEnumerable和IEnumerator接口以支持foreach语句
2009-07-08 23:25 2421在C#中,凡是实现了IEnumerator接口的数据类型都可以 ... -
IEnumerable与IEnumerator区别
2009-07-08 23:11 2614public interface IEnumerable{ ... -
IList、ICollection、IEnumerable 之辨析
2009-07-08 23:06 3139祖宗:IEnumerable 此接口只有一个方法 GetEn ... -
(c#)数据结构与算法分析 --栈与队列
2009-07-08 22:58 1809栈stack 栈是一种后进后出机制,它只允许访问 ... -
使用ASP.NET Global.asax 文件
2009-05-05 20:00 1140Global.asax 文件,有时候 ... -
ASP.NET VIEWSTATE初探
2009-05-05 17:55 1645一、 ViewState 的作用 与刚接触 ASP. ...
相关推荐
在标题和描述中提到的`("字段名") %>`和`("字段名") %>`是ASP.NET数据绑定表达式,它们用于在页面中显示数据或实现数据的双向绑定。 `Eval`方法用于单向数据绑定,它会从数据源中获取指定字段的值,并将其转换为...
在这个主题中,我们将深入探讨ASP.NET数据绑定的关键概念和技术。 首先,我们需要了解两种主要的数据绑定模式:声明式数据绑定和编程时数据绑定。声明式数据绑定通常在ASP.NET控件的属性中使用,如 `<asp:GridView>...
ASP.NET 数据绑定是ASP.NET开发中的核心概念,它允许开发者将网页控件(如GridView)与数据源(如SQL Server数据库)关联,实现数据的动态显示、编辑和管理。在这个"ASP.NET 数据绑定 GridView Demo"中,我们主要...
ASP.NET数据绑定技术是开发基于.NET框架的Web应用程序时的核心技术之一,主要用于在网页上动态显示和操作数据。本专题将深入探讨数据绑定原理、主要控件及其使用方法。 首先,数据绑定的基本原理是将数据源(如...
在这个"ASP.NET视频数据绑定"的主题中,我们将深入探讨数据绑定的概念、类型以及如何在实践中应用它们。 数据绑定在ASP.NET中的核心作用是简化了应用程序中数据展示和交互的过程。它允许开发者在不编写大量手动代码...
本篇文章将深入探讨如何使用ASP.NET来实现数据绑定控件的自定义分页功能。 一、数据绑定控件概述 在ASP.NET中,常见的数据绑定控件包括GridView、ListView、Repeater等。这些控件能够自动连接到数据源,如SQL ...
对于"asp.net treeview 绑定数据库"这个话题,我们将主要关注使用数据源控件来实现这一功能。常见的数据源控件,如SqlDataSource或EntityDataSource,都可以用来从数据库获取数据并自动绑定到TreeView。 步骤1:...
在ASP.NET中,数据绑定控件是Web开发中不可或缺的一部分,它们使得开发者能够方便地将数据库或其他数据源中的数据展示到网页上。本章节主要探讨的是数据绑定控件的使用,我们将围绕这一主题展开深入讨论。 首先,...
在这个“ASP.NET课件 数据绑定 主题和外观”中,我们将深入探讨两个关键概念:数据绑定和主题/外观设计,这些都是ASP.NET开发中的核心要素。 首先,让我们详细讨论数据绑定。在ASP.NET中,数据绑定是一个强大的特性...
这些DEMO覆盖了从基础到高级的ASP.NET数据访问和绑定技术,是学习和提升ASP.NET开发技能的重要资源。通过实践这些DEMO,开发者不仅可以掌握数据访问技术,还能理解如何在实际项目中应用这些技术,提升应用程序的性能...
在本实例中,我们将聚焦于ASP.NET中的数据绑定控件,包括DataList、GridView以及Repeater,这些都是在数据操作中非常重要的组件。 **DataList控件** DataList控件是一种高度可定制的数据展示控件,它可以显示数据...
在ASP.NET开发中,数据展示通常涉及到大量的数据处理和页面加载优化,特别是在处理大量数据时,数据...通过深入理解并利用这些源代码,开发者能够更好地掌握ASP.NET控件开发和数据绑定技术,提升自己的Web开发技能。
本教程将深入讲解如何在ASP.NET中使用TreeView控件进行数据绑定,帮助开发者构建交互式的树形视图。 1. **TreeView控件介绍** TreeView控件是一个服务器控件,它可以展示多级节点,每个节点可以包含子节点。通过...
本篇文章将深入探讨在ASP.NET 2.0框架下数据绑定的各种高级技巧及其应用场景。这些技巧不仅包括简单的数据绑定方法,还涵盖了多种数据源控件以及如何利用它们进行高效的数据处理。此外,本文还会介绍如何使用缓存来...
在ASP.NET中,数据绑定控件是用于展示和操作数据的核心元素。这些控件使得开发者能够轻松地将数据库或其他数据源中的数据与用户界面(UI)进行连接,从而实现动态、交互式的网页应用。以下是对标题和描述中提及的...
本教程将详细讲解如何在ASP.NET中直接绑定标准SQL数据库数据表,创建一个三级树结构。 首先,我们要理解ASP.NET中的TreeView控件。TreeView是一个用于显示分层数据的服务器控件,它可以以树状结构显示数据,用户...
在这个“ASP.NET 2.0 快速入门(2)”的主题中,我们将深入探讨ASP.NET 2.0的数据绑定机制,这是创建交互式、数据驱动Web应用程序的关键技术。 数据绑定是ASP.NET 2.0的核心特性之一,它允许开发者将服务器端数据源...
本篇将深入探讨ASP.NET 2.0的数据绑定高级技巧,帮助你提升开发效率和应用性能。 一、数据绑定概述 数据绑定是ASP.NET中一种重要的设计模式,它允许你在服务器端或客户端将数据源(如数据库、XML文件或对象集合)与...
总结,Asp.net的ReportViewer结合RDLC文件,提供了强大的报表设计和动态数据绑定能力。开发者可以根据实际需求,灵活地调整报表结构,从各种数据源获取数据,甚至实现交互式报表功能,提升用户体验。在实际项目中,...
在这个快速入门教程中,我们将深入探讨ASP.NET 2.0的数据绑定机制,这是创建动态网页的关键部分。 数据绑定是ASP.NET 2.0中的一个重要特性,它允许开发者将Web控件(如GridView、ListView、Repeater等)与数据源...