Chapter8 Data Binding

当最初发布的时候,AN的亮点之一就是能够将数据集和你的控件捆绑在一起,而不需要你写大量的代码。这些控件知道它们是数据捆绑的,而且能够将数据集合中的各个item渲染为合适的HTML。另外,你可以将这些控件和任意类型的数据源捆绑,从简单的数组到复杂的Oracle数据库查询结果。这是从经典ASP的一个飞越,那时候开发者是需要负责所有的数据访问代码,遍历一个记录集合,为每个数据渲染合适的HTML。
这一章探索服务器端的数据控件。向你展示如何方便的使用数据源控件来绑定数据到数据绑定控件。这一章同样定睛于AN的数据绑定列表控件。最后你会看到内嵌数据绑定语法和XML数据绑定语法。

DATA SOURCE CONTROLS

在AN1.0/1.1,你通常需要码一些数据访问代码获取一个DataReader或者DataSet对象来执行数据绑定的操作。然后你将数据对象和服务器控件例如DataGrid,DropDownList,或者ListBox捆绑。如果你想要更新或者删除捆绑的数据,你需要负责码代码来完成这些操作。下面的代码展示了数据绑定的操作在AN1.0/1.1是如何完成的。

SqlConnection conn = new SqlConnection();
SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet(); da.Fill(ds);
DataGrid1.DataSource = ds; DataGrid1.DataBind();

自从AN1.0/1.1开始,AN通过使用数据源控件引入了另一个抽象层。这些数据源控件将地下的数据提供者给抽象了,例如SQL Data Provider或者OLE DB Data Provider。这意味着你不需要关心怎么样或者为什么这样使用这些数据提供者,取而代之的是让这些数据源控件承担这些繁重的工作。你只需要知道你的数据在哪,必要的话,为CRUD操作构建查询语句。
另外,因为这些数据源控件都是从Control类继承的,你可以像使用其他网页服务器控件一样使用这些数据源控件。例如,你可以以声明或者编程的方式定义和控制数据源的行为。事实上,尽管你可以从代码中控制这些数据源控件,在本章的大多数例子中会向你展示怎么样用声名的方式执行强大的数据库查询。
AN有一些内置的数据源控件,每一个对应一种特定的数据。
- SqlDataSource 可以访问任何数据源,只要该数据源有ADO.NET Provider。默认的该控件可以访问ODBC OLE DB SS Oracle
- LinqDataSource 可以使用LINQ查询访问任何LinqToSql对象
- ObjectDataSource 可以访问会返回数据的类
- XmlDataSource 可以访问XML文档,不管XML文档在内存中还是硬盘上
- SiteMapDataSource 可以访问sitemap数据
- AccessDataSource 可以访问Access 数据库
- EntityDataSource 可以访问Entity Framework models

所有这些数据源控件都是从DataSourceControl类或者HierarchicalDataSourceControl类继承的,这两个类都是从Control类继承的,实现了IDataSource和IListSource接口,这意味着虽然每一个控件都是被特定的数据使用而设计的,他们共享核心功能的基本模块。这页意味着你应该,你能够根据你的特定数据源的结构创建你自己的定制数据源控件。

SqlDataSource

如果你的数据存储在SQL Server,SQL Server Express,SQL Server LocalDb,SQL Server Compact或者Oracle中,这就是你应该使用的数据源控件。这个控件给你提供了快捷的配置向导,或者你可以通过修改控件的属性来完成配置。在这一部分,你会看到如何创建和配置一个SqlDataSource控件,也会看到如何过滤这些结果。在本书的后面部分,你看到如何跟其他的数据控件类似GridView配搭,你可以允许用户更新和删除数据。

Configuration a Data Connection

当一个控件被拽到网页上时,你需要告诉它应该使用哪个数据库连接,最简单的方法是通过使用配置数据源的向导。
当你完成你的数据连接配置后,代码类似于下面这样

<asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT * FROM [Customers]"
ConnectionString="<%$ ConnectionStrings:ConnectionString %>" />

你可以看到这个控件使用申明语法创建ConnectionString属性来配置这个连接,创建SelectCommand属性来执行查询。

Data Source Mode Property

当你建立起一个基本的SqlDataSource控件后,众多特征之一就是DataSourceMode供你配置。这个特征允许你告诉SqlDataSource控件当获取数据的时候,它应该使用一个DataSet或者DataReader。如果你选择使用DataReader,数据通过firehose模式获取,这是从你的数据源获取数据最有效和最快速的方法,因为DataReadeer不需要内存和处理开销。
选择使用DataSet模式使得数据源控件具有过滤,排序和分页功能而变得更加强大,它也启用了控件内置的缓存功能。

Filtering Data Using SelectParameters

当然,当你从数据源选择数据时,你可能不会想要所有的数据。你可能想要指明某些参数在你的查询中来限制返回的数据。在配直数据源向导中,你可以使用在查询语句中添加WHERE来完成。究其根本,这个向导在使用这个数据源控件的SelectParameters集合来生成参数,数据源控件在运行时使用这些参数过滤从查询中返回的数据。
SelectParameter集合由从Parameters类中继承的类型构成,数据源控件使用这些来生成动态的SQL查询。
下面的代码在数据源控件的SelectParameters集合中增加了一个QueryStringParameter,正如你看到的,这个SelectCommand已经被修改成为含有WHERE的版本。当你运行这个代码时,查询字符串的id域的值会被绑定到SelectCommand的@CustomerID占位符,允许你只选择那些CustomID域匹配查询语句的值。

<asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT * FROM [Customers]
WHERE ([CustomerID] = @CustomerID)" ConnectionString="<%$ ConnectionStrings:ConnectionString %>" DataSourceMode="DataSet">
<SelectParameters>
<asp:QueryStringParameter Name="CustomerID" QueryStringField="ID" Type="String">
</asp:QueryStringParameter> </SelectParameters>
</asp:SqlDataSource>

除了使用配置数据源向导来生成SelectParameters集合外,你可以手工的在markup中定义它们。这个数据源控件包含了另一种过滤结果的方法叫做FilterParameters,它提供了基本的过滤功能,但是使用了不同的技术。这个差别在本章后面将会提到。

Conflict Detection

这个特征允许你告诉控件你更新的值和数据库中的值是否有冲突。它允许你阻止用户覆盖彼此的更新。当这个特征被设为OverwriteChanges时,最后更新的获胜。
如果这个值设为CompareAllValues,这个控件比较数据库中的原有值和用户提供的值。如果控件检测到有冲突,它不更新数据库。
一个决定你的更新是否遇到并发的错误方法时通过测试控件Updated事件的AffectedRows特征。如下所示

protected void SqlDataSource1_Updated(object sender, SqlDataSourceStatusEventArgs e)
{
if (e.AffectedRows > 0)
this.lblMessage.Text = "The record has been updated"; else
this.lblMessage.Text = "Possible concurrency violation";
 }

Handling Database Errors

数据源控件的事件非常适合在你对数据库执行SQL命令时捕获和处理错误。例如,下面的例子演示了你如何使用SqlDataSource控件的Updated事件来处理数据库错误。

protected void SqlDataSource1_Updated(object sender, System.Web.UI.WebControls.SqlDataSourceStatusEventArgs e)
{
if (e.Exception != null) {
this.lblMessage.Text = e.Exception.Message;
e.ExceptionHandled = true; 
}

这个例子一个非常重要的部分就是将ExceptionHandled特征设定为true。默认的,这个特征返回false。因此,如果你检测到Exception特征不是null,你试图处理这个错误,但是这个异常仍然从应用中冒出来。将ExceptionHandled特征设为true告诉.NET你已经成功地处理了这个异常,现在继续执行没有问题。

AccessDataSource Control

LinqDataSource Control

和SqlDataSource很像,在你的应用对LinqToSql对象产生Linq查询。

EntityDataSource Control

这个控件专门为使用ADO.NET框架的应用所用。这个控件为网页上的数据控件处理选择,更新插入和删除操作同时启动数据的排序和分页。和其他数据源控件一样,你可以从工具箱拖出一个控件到设计面板上然后通过配置向导进行配置。
下面是一个配置向导产生的markup

<asp:EntityDataSource ID="EntityDataSource1" runat="server" ConnectionString="name=NorthwindEntities" DefaultContainerName="NorthwindEntities" EnableDelete="True" EnableFlattening="False" EnableInsert="True" EnableUpdate="True" EntitySetName="Customers" EntityTypeFilter="Customer">
</asp:EntityDataSource>

Using the QueryExtender for Complex Filters

尽管EntityDataSource和LinqDataSource控件都含有内置的过滤数据的功能。这些功能没有完全发挥LINQ providers生成包含过滤的查询的全部强大之处。这也是QueryExtender的来源,允许你定义复杂的搜索,数据范围的过滤,复杂的多列OrderBy从句,甚至完全定制的表达。
QueryExtender和其他任何数据源控件可以合作,只要该数据源控件实现了IQueryDataSource接口。默认的情况下,这个包括了LinqDataSource和EntityDataSource控件。
下面的例子掩饰了如何使用SearchExpression

<asp:EntityDataSource ID="EntityDataSource1" runat="server" ConnectionString="name=NorthwindEntities" DefaultContainerName="NorthwindEntities" EnableDelete="True" EnableFlattening="False" EnableInsert="True" EnableUpdate="True" EntitySetName="Customers" EntityTypeFilter="Customer">
</asp:EntityDataSource>
<asp:QueryExtender ID="QueryExtender2"
runat="server" TargetControlID=" EntityDataSource1"> 
<asp:SearchExpression SearchType="StartsWith"
DataFields="CustomerID"> 
   <asp:QueryStringParameter DefaultValue="A" QueryStringField="search" /> 
</asp:SearchExpression>
</asp:QueryExtender>

XmlDataSource Control

这个数据源控件提供了你一种绑定内存中或者物理硬盘中的XML文档的简单方法。这个控件给了你许多可调控特征。你可以提供一个XPath查询来选择一部分数据。
下面的例子显示了你如何从MSDN网站消耗一个RSS种子,将所有的item nodes绑定到一个列表空间,例如GridView。

<asp:XmlDataSource ID="XmlDataSource1" Runat="server" DataFile="http://msdn.microsoft.com/rss.xml" XPath="rss/channel/item" />

你的XML很多情况下不是一个物理文件,而是存储在你的应用内存中或者数据库中的一个字符串。这个控件提供了一个Data特征,接受XML字符串。注意如果DataFile和Data特征同时设定了,DataFile特征优先级高于Data特征。
除此之外,在某些场景下,你可能想要将XmlDataSource控件中的XML输出到其他对象或者甚至想要保存修改过的XML。这个空间提供了两种方法来完成这个。一个方法是GetXmlDocument,允许你导出XML。另一种方法是Save方法,使用这个方法暗含了你在DataFile特征中提供了文件路径。
XmlDatasource控件也提供了你很多专门的事件。

ObjectDataSource Control

这个控件允许你将数据控件直接绑定到中间层业务对象,这些业务对象可以硬编码或者自动的从OR映射中产生。
为了演示如何使用ObjectDataSource对象,请看下面的例子

SiteMapDataSource Control

这个控件允许你和存储在网站的SiteMap配置文件中的数据打交道。如果你的sitemap数据在运行会改变,也许机遇用户权利或者状态,这非常有帮助。
注意两个items

  • 这个控件不支持其他数据源控件存在的数据缓存选项,所以你不能自动的缓存你的sitemap数据
  • 这个SiteMapDataSource控件不像其他的数据源控件有配置向导。这是因为这个控件只能和SiteMap配置数据文件挂钩,其余的配置都不可能。

DATA SOURCE CONTROL CACHING

AN包含了一个非常棒的缓存设施,允许你在服务器端设定时间内缓存任何类型的对象。在22章你会学到更多AN缓存。
数据源控件利用了内置的缓存设施允许你容易的缓存它们的查询结果。每一个控件使用相同的基本语法来配置缓存,允许你生成简单的缓存政策,包括缓存方向,截止缓存和key依赖。

注意 SqlDataSource控件的缓存特色只有当你将DataSourceMode设定为DataSet才行,如果你设为DataReader,控件会抛出NotSupportedException
缓存的持续时间可以被设定,或者你可以将其设为Infinite,使得缓存数据永远不截止。洗面演示了你如何增加缓存特色到一个数据源控件。

<asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT * FROM [Customers]" ConnectionString="<%$ ConnectionStrings:ConnectionString %>" DataSourceMode="DataSet" ConflictDetection="CompareAllValues" EnableCaching="True" CacheKeyDependency="SomeKey" CacheDuration="Infinite" /> 

DATA-BOUND CONTROLS

这个部分看看数据绑定控件,用来展示,生成,编辑和删除数据。

GridView

GridView控件是一个强大的数据网格控件,允许你展示数据的整个集合,增加排序和分页,执行内嵌的编辑。
当GridView在渲染的过程中,它释放了很多事件,你可以用来改变控件的输出或者对你的应用添加定制的逻辑。
RowDataBound事件特别游泳,允许你对数据源中的每个item在绑定到GridView的过程中注射逻辑。下面的例子演示了你如何使用这个事件来检查将要被绑定到当前grid row的数据并且插入特别的逻辑。在这个栗子中,检查item的区域值是否为Null。如果null值被发现,逻辑改变GridView row的ForeColor和BackColor。

<script runat="server">
protected void GridView1_RowDataBound(object sender,
GridViewRowEventArgs e)
{
if (e.Row.DataItem != null) {
System.Data.DataRowView drv = (System.Data.DataRowView)e.Row.DataItem;
if (drv["Region"] == System.DBNull.Value) {
e.Row.BackColor = System.Drawing.Color.Red;
e.Row.ForeColor = System.Drawing.Color.White; }
} }
</script>

Handling Null and Empty Data Conditions

在某些情况下,绑定到GridView的数据源可能不包含任何数据,在这些情况下,你可能想要给终端用户一些反馈,通知它们这个情况。GridView包括了两种机制来完成。
第一个选项是使用EmptyDataText特征。这个特征允许你指定文本字符串,当GridView没有数据绑定的时候显示。当AN网页装载且GridView发现在数据源中没有数据,它生成一个特殊的DataRow包含了这个EmptyDataText值,显示给用户。下面的例子显示了你如何在GridView中增加这个特征。

<asp:GridView ID="GridView1" Runat="server" DataSourceID="SqlDataSource1" DataKeyNames="CustomerID" AutoGenerateColumns="True"
EmptyDataText="No data was found using your query"></asp:GridView>

另一个选项是使用EmptyDataTemplate控件模版来完全自定制特殊的行,让用户看。

注意 一个控件模版只是简单的容器,给你这个增加内容的能力,例如文本,HTML控件甚至是AN控件。这个GridView控件提供给了你应对不同情形的模版,包括EmptyDataTemplate模版。

Column Sorting

对数据排序的能力是用户浏览数据需要的基本工具之一。只要将GridView中的AllowSorting属性设为true就能启动排序。这个控件为你照料一切的排序逻辑。下面的例子展示了如何添加这个属性。

<asp:GridView ID="GridView1" Runat="server" DataSourceID="SqlDataSource1" DataKeyNames="CustomerID" AutoGenerateColumns="True" AllowSorting="True">
</asp:GridView>

当启用排序后,你会看到grid的列表头都变成了超链接。GridView排序可以处理降序和升序。GridView也包括一个Sort方法能够接受多个SortExpression来启用多列的排序,下面的例子显示了如何使用GridView的排序事件来实现多列排序。

<script runat="server">
protected void GridView1_Sorting(object sender,
GridViewSortEventArgs e)
{
string oldExpression = GridView1.SortExpression; string newExpression = e.SortExpression;
if (oldExpression.IndexOf(newExpression) < 0)
{
if (oldExpression.Length > 0)
e.SortExpression = newExpression + "," + oldExpression;
else
e.SortExpression = newExpression;
} else {
www.it-ebooks.info
e.SortExpression = oldExpression; }
} 
</script>

Paging GridView Data

这个控件也允许你增加另一个常用特色-分页。启用分页很简单,将AllowPaging特征设为true或者在控件的smart tag中对Enable Paging check box打上勾勾。这个控件默认以10条记录分页,将分页器放在网格的地步。下面的例子说明了如何让GridView启用排序和分页的方法。

<asp:GridView ID="GridView1" Runat="server" DataSourceID="SqlDataSource1" DataKeyNames="CustomerID" AutoGenerateColumns="True" AllowSorting="True" AllowPaging="True"></asp:GridView>

这个控件包含了很多让你自定制分页的特征。例如,你可以使用控件的PageSize属性来控制每一页显示的记录数目。PagerSettings Mode属性允许你指定分页器如何显示,使用不同的分页器模式包括NextPrevious NextPreviousFirstLast Numeric 或者NumericFirstLast。另外的PagerStyle元素让你指定pager的字体颜色,大小类型。

Customizing Columns in the GridView

你想要在网格中显示的数据通常不是简单的文本数据,而是你想用其他控件显示甚至不想显示的数据。
如果你将你网格配置为根据数据源的结构自动产生列,网格为数据源暴露的每个公共特征生成fields。除了返回Boolean类型的特征,其余的特征,GridView默认使用其BoundField类型。对于布尔类型,这个控件默认使用CheckBoxField。
这些默认的行为可能在你的应用中不是最佳的。例如,你在存储所有用户的网站地址,你想要用户姓名那一列可以显示为超链接,允许你的用户直接跳转到其网站。GridView包含了一些专门的字段类型,很容易的显示超链接,buttons。
你可以有两种方法配置GridView中的列-通过控件的smart tag或者在Source View中直接编辑列。
点击GridView smart tag的Edit Column链接打开了Fields窗口。这里你可以改变列的可视性,头文本,通常的风格选项,和许多列的其它特征。
点击GridView smart tag的Add New Column显示了Add Field对话框。这个对话框含有允许你增加新列到你的网格的选项。取决于你从下拉菜单中选择的column field type,对话框会呈现给你列类型的合适选项。
Add Field对话框让你能够选择field types中的一种。
- BoundField 显示数据源的一个域的值。这是GridView控件的默认列类型
- CheckBoxField 在GridView中为每个item显示一个勾选框。这个列域类型常用来显示Boolean值的域。
- HyperLinkField 将数据源中的一个域的值显示为超链接URL。这个列域类型允许你将另一个域绑定为超链接文本。
- ButtonField 为GridView中的每个item显示一个命令button或者命令链接。这允许你生成自定义的按钮或者链接控件,例如Add或者Remove按钮。
- CommandField 将代表的域显示为命令按钮或者链接来执行select,edit,insert,或者delete操作。
- ImageField 当域中的数据代表了一张图片或者URl 自动的显示一张图片。
- TemplateField 根据指定模板,为GridView控件中的每个item显示用户定义的内容。这个列域类型允许你生成自定制的列域。
在之前的例子中,当你想要终端用户连接到用户的网站,你需要从下拉菜单中添加HyperLinkField。这个Add Field对话框相应地改变,让你能够输入超链接信息,包括URL,data field,和格式化字符串。
你也可以在Source view中修改grid的列,人工的增加或者编辑任何field types。下面显示了你如何在Source view中添加合适的markup来生成HyperLinkField

 <asp:HyperLinkField DataTextField="CompanyName"
 HeaderText="CompanyName" DataNavigateUrlFields="CustomerID,Country"
 SortExpression="CompanyName"
 DataNavigateUrlFormatString=
 http://www.example.com/Customer.aspx?id={0}&country={1} />

当你在Source view中添加field时,你需要确保从你的data source中指定一个特征名字到这个域上。每一个域类型曝露了一个不同的特征(特征集合)允许你定义域和数据源之间的联系。在之前的例子中,你可以看到这个HyperLinkField的DataNavigateUrlFields特征允许你提供逗号分隔的数据源特征名字。这允许你指定多个数据源值来绑定到这个列。你可以在你的格式字符串中使用这些域来传递两个查询字符串参数。

Using the TemplatedField Column

在GridView控件中一个关键的列类型就是TemplateField列。这个列类型允许你通过定义模板完全的自定制列的内容。
这个TemplateField提供了你6个模板,允许你定制列的不同区域或者状态。例如编辑状态。
- ItemTemplate 用来显示一个TemplateField cell的模板
- AlternatingItemTemplate 用来显示一个alternate TemplateField cell的模板
- EditItemTemplate 在edit状态中显示一个TemplateField的模板
- InsertItemTemplate 在插入状态中显示一个TemplateField的模板
- HeaderTemplate 用来显示TemplateField头部的模板
- FooterTemplate 用来显示TemplateField底部的模板
为了在GridView中使用TemplateField,使用Add Field对话框添加列类型到网格中。asp:TemplateField 标签是作为列中能包含的不同模板的容器。在template中添加内容,你可以使用VS2012设计面板的模板编辑特色或者手工的在Source view中往TemplateField元素中添加内容。
ItemTemplate控制了列的每个cell的默认内容。下面演示了如何使用ItemTemplate来定制列的内容。

<asp:TemplateField HeaderText="CurrentStatus">
<ItemTemplate>
<table>
<tr>
<td >
<asp:Button ID="Button2"
runat="server" Text="Enable" /></td>
<td >
<asp:Button ID="Button3"
runat="server" Text="Disable" /></td>
</tr>
</table>
</ItemTemplate>
</asp:TemplateField>

在这个例子中,这个ItemTemplate包含了一个HTML table和AN 按钮控件。
因为这个GridView控件是data bound,你同样可以使用data-binding表达例如Eval Xpath或者Bind语句。下面显示了你如何使用data-binding表达使用Eval方法设置Button control的text field。更多关于data-binding表达会在本章的后面介绍。

<asp:TemplateField HeaderText="CurrentStatus">
 <ItemTemplate>
 <table>
 <tr>
 <td>
 <asp:Button ID="Button2" runat="server"
 Text='<%# "Enable " + Eval("CustomerID") %>' />
 </td>
 <td>
 <asp:Button ID="Button3" runat="server"
 Text='<%# "Disable " + Eval("CustomerID") %>' />
 </td>
 </tr>
 </table>
 </ItemTemplate>
</asp:TemplateField>

在TemplateField中其它常见模板是插入模板和编辑模板。这些模板是当row进入编辑或者插入模式时使用的。

Editing GridView Row Data

用户不仅想要在浏览器中观看数据,他们也想要有能力编辑数据并将变化反应在数据源。当和数据源控件结合的时候,GridView控件使得绑定该grid的数据编辑相当简单,为了演示启动编辑是多么简单,你可以修改这个SqlDataSource和GridView控件来允许用户编辑消费者的数据。
注意尽管在这章,你主要聚焦于更新和SqlDataSource连接的GridView控件中的数据,但是当GridView和其他数据源控件连接的时候,你也可以更新它们的数据。这个使得终端用户将grid view设为插入或者编辑或者删除状态的配置是一样的,不管数据源控件是什么。但是对数据源将数据变化反馈到下面的数据store的配置是和数据源控件相关的。如果你使用的数据源控件不是SqlDataSource,你可以查阅本章的这部分。

Configuration the SqlDataSource for Updates

首先修改SqlDataSource控件来添加一个UpdateCommand,这可以通过配置Data Source wizard或者手工的在Source view中添加markup。这个特征告诉数据源控件当需要更新的时候应该执行哪个SQL命令。

<asp:SqlDataSource ID="SqlDataSource1" Runat="server"
SelectCommand="SELECT * FROM [Customers]"
ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
DataSourceMode="DataSet"
UpdateCommand="UPDATE [Customers] SET [CompanyName] = @CompanyName,
[ContactName] = @ContactName, [ContactTitle] = @ContactTitle,
[Address] = @Address, [City] = @City, [Region] = @Region,
[PostalCode] = @PostalCode, [Country] = @Country,
[Phone] = @Phone,[Fax] = @Fax
WHERE [CustomerID] = @ CustomerID">
</asp:SqlDataSource>

注意这个UpdateCommand包括了许多占位符,例如@CompanyName,@Country,@Region和@CustomerID。这些占位符代表当更新row的时候从GridView来的信息。每一个占位符对应一个定义在SqlDataSource的UpdateParameters集合中的Parameter元素。这个UpdateParameters集合,在下面的代码中。

<UpdateParameters>
<asp:Parameter Type="String" Name="CompanyName"></asp:Parameter>
<asp:Parameter Type="String" Name="ContactName"></asp:Parameter>
<asp:Parameter Type="String" Name="ContactTitle"></asp:Parameter>
<asp:Parameter Type="String" Name="Address"></asp:Parameter>
<asp:Parameter Type="String" Name="City"></asp:Parameter>
<asp:Parameter Type="String" Name="Region"></asp:Parameter>
<asp:Parameter Type="String" Name="PostalCode"></asp:Parameter>
<asp:Parameter Type="String" Name="Country"></asp:Parameter>
<asp:Parameter Type="String" Name="Phone"></asp:Parameter>
<asp:Parameter Type="String" Name="Fax"></asp:Parameter>
<asp:Parameter Type="String" Name="CustomerID"></asp:Parameter>
</UpdateParameters>

每一个parameter使用两个特征来生成一个连接,一个是Name,对应database的column 名字,还有一个Type,是这个数据库列的数据类型。在这个例子中,所有的参数都是String类型。
记住你也可以使用本章提到过的任意参数类型,例如ControlParameter或者QueryParamter。

Configuring GridView for Updates

现在你已经配置了SqlDataSource用来更新,你需要配置GridView。GridView有两种内置的方法来将某一行放置为edit模式-AutoGenerateEditButton特征和CommandField。
当AutoGenerateEditButton特征设为true时,这告诉了grid为每一行添加一个带有edit按钮的列,点击这个按钮将所关联的行放置为edit模式。下面演示了如何增加AutoGenerateEditButton属性在GridView控件。

<asp:GridView ID="GridView1" Runat="server"
DataSourceID="SqlDataSource1" DataKeyNames="CustomerID"
AutoGenerateColumns="True" AllowSorting="True" AllowPaging="True"
AutoGenerateEditButton="true" />

GridView控件也包括了AutoGenerateSelectButton和AutoGenerateDeleteButton特征,允许你轻松地添加行选择和行删除特性到grid。
这个CommandField列是一个特殊的field type允许你让终端用户执行不同的命令。下面的代码配置了这个CommandField来允许用户将row设置为edit模式。

<asp:CommandField ShowHeader="True" HeaderText="Command"
ShowEditButton="True" />

这个允许你将命令显示为链接,按钮甚至图片。
现在如果你浏览你的网页,你会看到一个新的编辑列。点击这个编辑列允许用户编辑那个数据行的内容。
这个CommandField元素有属性能够使你控制列里面显示什么。你可以描绘这一列是不是显示Cancel,Delete,Edit,Insert或者Select。
为了完成GridView编辑的配置,你需要确定grid知道哪个数据库表的列是它的primary key。你可以使用GridView的DataKeyNames特征。

<asp:GridView ID="GridView1" Runat="server"
DataSourceID="SqlDataSource1" DataKeyNames="CustomerID"
AutoGenerateColumns="False" AllowSorting="True" AllowPaging="True">

如果这个表的primary key不止一列,你可以使用逗号分隔的列表指定。
你通过设定列的ReadOnly特征指明grid的哪些列允许编辑。

<asp:BoundField DataField="CustomerID" HeaderText="CustomerID"
SortExpression="CustomerID" ReadOnly="True" />

现在,如果你再次浏览网页然后点击Edit按钮,你应该看到ID列不能编辑。

Handling Errors When Updating Data

通过使用RowUpdated时间,你可以对GridView进行错误检查。下面的代码演示了如何在更新数据后进行错误检查。

<script runat="server">
protected void GridView1_RowUpdated(object sender,
GridViewUpdatedEventArgs e)
{
if (e.Exception != null)
{
this.lblErrorMessage.Text = e.Exception.Message;
}
}
</script>

这个RowUpdated事件参数包含了一个Exception特征。

Using the TemplateField’s EditTemplate

在本章的前面,我们介绍了TemplateField和它所包含的一些模板。这些模板之一就是EditTemplate,当row进入编辑模式时,grid使用EditTemplate列。使用EditTemplate允许你完全定制用户的的数据编辑体验。举个例子,Region列的更好的编辑体验应该是提供一个下拉列表而不是简单的文本框,简单的文本框是BoundField的默认编辑体验。
为了完成这个,你只是将Region的列从BoundField改为TemplateField,然后添加ItemTemplate和EditItemTemplate。在EditItemTemplate,你可以添加一个DropDownList控件,然后提供数据绑定信息,这个控件就绑定到区域的唯一列表。下面的代码显示了你如何添加EditItemTemplate和ItemTemplate到这个GridView。

asp:TemplateField HeaderText="Country">
 <ItemTemplate><%# Eval("Country") %></ItemTemplate>
 <EditItemTemplate>
 <asp:DropDownList ID="DropDownList1" runat="server"
 DataSourceID="SqlDataSource2"
 DataTextField="Country" DataValueField="Country">
 </asp:DropDownList>
 <asp:SqlDataSource ID="SqlDataSource2" runat="server"
 ConnectionString=
 "<%$ ConnectionStrings:ConnectionString %>"
 SelectCommand="SELECT DISTINCT [Country] FROM [Customers]">
 </asp:SqlDataSource>
 </EditItemTemplate>
</asp:TemplateField>

一个简单的Eval数据绑定表达在ItemTemplate中显示行默认模式下列的值。在EditTemplate中,一个下拉的控件绑定到SqlDataSource。
为了在下拉控件中显示当前选择的国家,你使用RowDataBound event。

protected void GridView1_RowDataBound(object sender,
GridViewRowEventArgs e)
{
// Check for a row in edit mode.
if ( (e.Row.RowState == DataControlRowState.Edit) ||
(e.Row.RowState == (DataControlRowState.Alternate |
DataControlRowState.Edit)) )
{
System.Data.DataRowView drv =
(System.Data.DataRowView)e.Row.DataItem;
DropDownList ddl =
(DropDownList)e.Row.Cells[8].
FindControl("DropDownList1");
ListItem li =
ddl.Items.FindByValue(drv["Country"].ToString());
li.Selected = true;
}
}

为了设置DropDownList的值,首先查验当前绑定的GridViewRow在编辑模式。为了正确群定当前RowState,你需要对RowState作出多个比较。这个RowState可能处于多个状态-举个例子,alternate和edit-所以,你需要使用位级别的比较来决定GridViewRow是否在编辑模式。
当该行被确定是在编辑状态后,定位这个DropDownList控件。找到DropDownList后,定位DropDownList ListItem,将其Selected特征设为True。
你同样需要一个GridView事件将DropDownList控件的值反应到GridView中。使用RowUpdaing事件。

protected void GridView1_RowUpdating(object sender,
GridViewUpdateEventArgs e)
{
GridViewRow gvr =
this.GridView1.Rows[this.GridView1.EditIndex];
DropDownList ddl =
(DropDownList)gvr.Cells[8].FindControl("DropDownList1");
e.NewValues["Country"] = ddl.SelectedValue;
}

在这个事件中,你使用EditIndex确定处于编辑模式GridViewRow。当你找到这行后,定位DropDownList控件。找到这个控件后,只是将控件的SelectedValue添加到GridView控件的NewValues集合。

Deleting GridView Data

从GridView中删除数据比编辑数据要简单地多。只要在SqlDataSource和GridView中加一些代码就能使你从table中删除一整行。
和编辑按钮一样,你要添加一个Delete按钮到grid,这通过设定AutoGenerateDeleteButton特征为True或者使用CommandField。

<asp:GridView ID="GridView2" Runat="server"
DataSourceID="SqlDataSource1" DataKeyNames="CustomerID"
AutoGenerateColumns="True" AllowSorting="True" AllowPaging="True"
AutoGenerateEditButton="true" AutoGenerateDeleteButton="true"/>

对SqlDataSource的调整也是微不足道的。和UpdateCommand一样,这个DeleteCommand特征使用命名参数来决定删除哪一行。
记住就跟你更新数据一样,当你删除数据校验数据库错误是很好的编码规范。

protected void GridView1_RowDeleted(object sender,
GridViewDeletedEventArgs e)
{
if (e.Exception != null)
{
this.lblErrorMessage.Text = e.Exception.Message;
e.ExceptionHandled = true;
}
}
protected void SqlDataSource1_Deleted(object sender,
SqlDataSourceStatusEventArgs e)
{
if (e.Exception != null)
{
this.lblErrorMessage.Text = e.Exception.Message;
e.ExceptionHandled = true;
}
}

注意两个事件都提供了Exception特征。如果这些特征不为空,异常发生了。如果你选择处理异常,你要将ExceptionHandled特征设定为True。否则,这个异常会继续抛给用户。

DetailsView

DetailsView控件是一个data-bound控件,允许你一次和单个数据记录打交道。尽管GridView很适合浏览数据集合,但是有许多场景,你想要展示一条记录而不是整个集合。DetailsView控件允许你这么做并且提供了许多GridView同样的数据操作和显示特性,包括分页,上传,插入和删除数据。
开始使用DetailsView,将这个控件拽到设计面板上。和GridView一样,你可以使用DetailsView的smart tag来生成和设置这个控件的data source。在这个部分的例子使用之前部分GridView的SqlDataSourceControl。下面的代码演示了一个DetailsView绑定到了一个SqlDataSource控件。

<asp:DetailsView ID="DetailsView1" Runat="server"
DataSourceID="SqlDataSource1" DataKeyNames="CustomerID"
AutoGenerateRows="True"></asp:DetailsView>
<asp:SqlDataSource ID="SqlDataSource1" Runat="server"
SelectCommand="SELECT * FROM [Customers]"
ConnectionString=
"<%$ ConnectionStrings:ConnectionString %>"
DataSourceMode="DataSet">
</asp:SqlDataSource>

如果你想要只显示一条记录,你可能想要改变SqlDataSource控件的SelectCommand,让它只返回一个消费者,而不是返回所有的消费者。然而,如果你从数据库返回不止一个对象,你可以允许你的用户通过设定DetailsView的AllowPaging特征为True来达到分页的效果。如下面的代码所示。

<asp:DetailsView ID="DetailsView1" Runat="server"
DataSourceID="SqlDataSource1" DataKeyNames="CustomerID"
AutoGenerateRows="True" AllowPaging="true"></asp:DetailsView>

你可以在DetailsView的smart tag中勾选启用分页或者在源码视角添加这个特征。和GridView一样,DetailsView控件也允许你定制化pager。

Customizing the DetailsView Display

你可以通过选择控件显示的字段来定制化DetailsView控件的外表。默认情况下,这个控件显示它绑定的数据源轭每一个公共特征。然而,使用和GridView控件一样的基本语法,你可以指定显示某些特征。

Using the DetailsView and GridView Together

下一步,这个部分看看一个常用的master/detail显示场景,这个场景使用GridView和DetailsView。在这个例子中,你使用GridView显示数据的master view,使用DetailsView显示GridView被选择的行的详细数据。
为了了解这是怎么做到的,看看SqlDataSource2。一个FilterExpression用来过滤从SelecteCommnd返回的数据。在这种情况下,FilterExpression的值设为Customer=’{0}’,表示这个控件应该使用CustomerID来过滤返回的数据。
FilterExpression值指定的参数,CustomerID,定义在SqlDataSource控件的FilterParameters集合。这个例子使用一个asp:ControlParameter指定GridView控件的SelectedValue特征来填充这个参数的值。

SelectParameters versus FilterParameters

你可能已经注意到之前的例子,FilterParameters看上去和SelecteParameters的功能一样。尽管两个产生了一样的结果,它们使用了不同的方法。正如你在前面部分看到的,使用SelectParameters允许开发者在SelectCommand的WHERE从句中注入值。这限制了从SQL Server返回的行还素和数据源控件在内存中掌握的数据,这个方法的优势是限制了从SQL返回的数据量,你能使你的应用变得快速并且减少它所消耗的内存。这个方法的劣势是你受限于SQL查询返回的少量数据。
FilterParameters,另一方面,并不使用WHERE从句,相反的,它要求从server返回所有的数据,然后对数据源控件在内存的数据做一个过滤。这个过滤方法的劣势是得从data store返回更多的数据。然而,在某些情况,比如你要在一个很大的数据堆上执行许多过滤。这是一个优势,因为你不需要每次都寻味你的数据商店。所有的数据由这个数据源控件存储在内存中。

Inserting,Updating,and Deleting Data Using DetailsView

在DetailsView中插入数据和在GridView控件中插入数据一样。为了在DetailsView中插入数据,仅仅添加AutoGenerateInsertButton特征到DetailsView的控件。

<asp:DetailsView ID="DetailsView1" runat="server"
DataSourceId="SqlDataSource1" DataKeyNames="CustomerID"
AutoGenerateRows="True" AutoGenerateInsertButton="True" />

然后添加InsertCommand和对应的InsertParameters元素到这个SqlDataSource控件。

ListView

AN包括了另一个列表形式的控件,减少了高度结构化的GridView和其它一些没有结构化的控件例如DataList和Repeater之间的代沟。
在过去,许多开发者想要一个网格风格的数据控件的时候就会使用GridView,因为它使用方便并且提供了强大的特色例如数据编辑,分页和排序。不幸的是,随着开发者对这个控件的了解更加深入,他们越来越发现控制GridView产生的HTML输出变得越来越困难。如果你想要减少这个控件产生的markup,这会变成一个问题。
另一方面,许多开发者被DataList和Repeater吸引,由于他们能够在这些控件的渲染上有更多的控制权。这些控件几乎不包含排版记号,给了开发者充分的自由来对他们的数据进行排版。不幸的是,这些控件缺少GridView的一些基本特色,例如分页和排序,在Repeater中,缺少数据编辑的概念。
这就是ListView可以派上用场的地方。这个控件本身不发射任何runtime的HTML,取而代之的是,它依赖代表这个控件不同区域的11个控件模板和这些区域的可能状态。在这些模板内,你可以放置design time控件产生的markup,或者由开发者生成的markup,无论哪种情况,开发者持有完全的控制能力,无论是对控件内各个数据item的markup,还是整个控件的布局的markup。另外,因为这个控件理解和处理数据编辑和分页,你可以让这个控件做很多数据管理工作,允许你定睛于数据显示上。

Getting Started with the ListView

为了开始使用ListView,将这个控件拖到设计面板,分配一个数据源控件给它,跟之前任何data-bound列表控件一样。当你分配了数据源后,你会发现没有你所期待的design-time 排版预览。这是因为,默认情况下,ListView没有定义的排版,完全靠你来定义这个控件的排版。事实上,这个控件design-time渲染甚至告诉你需要定义至少一个ItemTemplate和LayoutTemplate来使用这个控件。这个LayoutTemplate是作为控件的root template,这个ItemTemplate是作为控件里的每一个dataItem的模板。
你有两种选项来定义ListView所需要的模板。你要么在ListView的smart tag中改变Current View Option来直接编辑模板,要么你可以从ListView的smart tag中选择一个已经定义过的排版。改变现在视角允许你观察运行的时候每个模板的样子。
第二个选项,可能是最简单的选项,从Configure ListView对话框中选择预先定义的排版模板。为了打开这个对话框,从smart tag中点击ConfigureListView选项。你会面对一个让你从几个预先定义的排版中选择的对话框,选择不同的风格选项,甚至配置基本的行为选项例如编辑和分页。
这个控件包括了5中排版类型和4中风格选项。

ListView Templates

当你对ListView应用一个排版模版后,如果你在VS的Source window,你可以看到为了提供这个排版,这个控件生成了一大堆markup。这个markup是基于你所选择的排版生成的。
如果你仔细检查这个Grid layout生成的markup,你会看到,默认情况下,这个控件生成了7个控件模板:ItemTemplate,AlternatingItemTemplate,SelectedItemTemplate,InsertItemTemplate,EditTemplate,EmptyDataTemplate和LayoutTemplate。这些是这个控件曝露的11个控件的一部分,你可以用来提供这个控件不同状态的markup。选择一个不同的预定义排版选项使得这个控件生成不同的模板集合。当然,你总是可以手工地添加或者移除这些模板。

  • ItemTemplate 为控件中的每个data Item 提供一个UI
  • AlternatingItemTemplate
  • SelectedItemTemplate 为当前选定的data item提供一个UI
  • InsertItemTemplate 为要插入的data item提供一个UI
  • EditItemTemplate 为在控件中编辑的data item提供一个UI
  • EmptyItemTemplate 在当前页最后一部分没有数据显示的情况下提供够一个UI
  • LayoutTemplate 作为ListView的根容器,用来控制data items的总的布局。
  • GroupSeparatorTemplate 为groups提供separator UI
  • GroupTemplate 为分组的内容提供UI
  • ItemSeparatorTemplate 为data item之间提供separator UI

模板的使用允许ListView控件对markup部分和状态持有基本的信息,而且允许你对ListView的UI有完全的控制。

ListView Data Item Rendering

虽然ListView非常的灵活,允许你对其绑定数据的显示有完全的控制权,但是它有一些基本的结构定义了之前描述的模板之间的联系。正如之前描述的,最基本的,这个控件要求你定义两个模板,一个LayoutTemplate和ItemTemplate。LayoutTemplate是根控件模板,因此也是你定义整个数据集合布局的地方。
例如,如果你检查Grid布局生成的markup,你会看到LayoutTemplate包含了一个table元素,一个tr定义和为每个列的td元素。
ItemTemplate,另一方面,是你为每个data item定义布局的地方。如果你再一次看看这个Grid layout生成的markup,ItemTemplate是一个table row元素,紧跟着一系列td元素。
当这个ListView渲染的时候,它知道这个ItemTemplate应该渲染在LayoutTemplate中,但是需要有一个机制告诉这个控件,到底在哪里放置这个ItemTemplate。这个ListView控件通过在LayoutTemplate中寻找一个item container来完成这个。这个item container是一个HTML container元素,runat=”server”,id属性设为itemContainer。这个元素可以是任何有效的HTML container元素,虽然如果你检查默认的Grid LayoutTemplate,你会看到它使用tbody元素

<tbody id="itemContainer">
</tbody>

甚至这个itemContainer元素的id也能够配置。虽然默认情况下,这个控件会试图定位id设为itemContainer的元素,你可以改变控件查询的id值,改变控件的ItemContainerID特征。
如果这个控件不能定位合适的作为item容器的HTML元素,它会抛出一个异常。
ListView不仅使用itemContainer元素来放置ItemTemplate,还使用任意item级别的模板,例如AlternativeItemTemplate,EditTemplate,EmptyItemTemplate,InsertItemTemplate,ItemSeparatorTemplate和SelectedItemTemplate。在渲染过程中,它仅仅将合适的item template放进item container,取决于data item的状态(被选择,编辑或者转换)

ListView Group Rendering

除了item container,ListView也支持另一种container类型,group container。这个group container和Group Template一起允许你将一个大的data items组分割成更小的集合。每一个group items的数量由控件的GroupItemCount特征确定。当一些item模板被渲染后,你想要输出某些另外的HTML,这种情形下,这非常有用。当使用GroupTemplate时,存在之前那个部分讨论的问题。在这种情况下,然而,引进GroupTemplate意味着你有三个关联模板:ItemTemplate和GroupTemplate,GroupTemplate和LayoutTemplate。
当ListView渲染时,它会检查有没有定义一个GroupTemplate。如果该控件发现了一个GroupTemplate,它校验LayoutTemplate中是否提供了一个group container。如果你定义了GroupTemplate,这个控件要求你定义一个group container。否则,它会抛出一个异常。这个group container和item container的工作原理一样,除了这个container的id值应该是groupContainer,而不是itemContainer。和itemContainer一样,通过转换这个控件的GroupContainerID特征。
你可以看看ListView的Tiled layout生成的markup来看看group container的例子。

<table id="groupContainer" runat="server" border="0" style="">
</table>

在group container定以后,你需要定义一个item container,但是不是在LayoutTemplate中,而是GroupTemplate。再一次看看这个Tiled layout,你可以看到在GroupTemplate中,它定义了一个table row。

<tr id="itemContainer" runat="server">
</tr>

Using the EmptyItemTemplate

当使用GroupTemplate时,要记住绑定到ListView控件的data items的数量可能不能被GroupItemCount整除。为了解决这个问题,ListView含有一个EmptyItemTemplate。当你使用GroupTemplate并且没有足够的data items时,就会使用EmptyItemTemplate。
举个例子,如果ListView包含了4个data items,但是这个GroupItemCount设置为3,每个group会有3个ItemTemplate被渲染。这意味着对于第二个group,只剩下一个data item需要渲染,所以,这个控件会使用EmptyItemTemplate。

ListView Data Binding and Commands

因为ListView在运行时不生成任何的排版markup并且未包含任何自动字段生成逻辑,每一个模板使用标准的AN内嵌数据绑定语法来定位每个data item的值。内嵌的数据绑定语法在本章的后面会涉及到。
你可以看一个内嵌绑定的例子,当你将这个控件的layout设为grid layout时。在这个例子中,绑定的data source的每一列使用一个AN label,它的text特征设为一个data-binding 评估表达式

<asp:Label ID="ProductNameLabel" runat="server"
Text='<%# Eval("ProductName") %>' />

因为这个控件使用这个灵活的模型显示绑定的数据,你可以使用它来将数据放置在模版的任意地方,甚至使用AN数据绑定的特色将绑定的数据进行操作后再显示。
每一个显示绑定数据的ListView模板使用相同的AN绑定语法,只是提供不同的模板而已。例如,如果在Grid layout中启动了编辑,你会看到EditItemTemplate带了ItemTemplate。

<asp:TextBox ID="ProductNameTextBox" runat="server"
Text='<%# Bind("ProductName") %>' />

这个灵活性允许你选择你让你的用户可以编辑的是哪些?你可以以下拉代替标准的AN文本框,甚至是第三方的编辑控件。
为了让ListView显示EditTemplate,这个控件使用GridView相同的命令概念。ItemTemplate提供了三个命令你可以用来改变data item的状态

  • Edit 将具体的data item放置为edit模式,然后显示这个data item的EditTemplate。
  • Delete 从底下的数据源删除这个具体的data item
  • Select 将ListView控件的选择索引设置为这个具体的data item的索引

    这些命令和AN 按钮控件的CommandName特征连起来使用。当你启用ListView的编辑和删除功能时,你可以看到这些命令使用在ListView默认grid layout的ItemTemplate中。这样做生成了一个新的列,带有编辑和删除按钮。

    <asp:Button ID="DeleteButton" runat="server"
    CommandName="Delete" Text="Delete" />
    <asp:Button ID="EditButton" runat="server"
    CommandName="Edit" Text="Edit" />

ListView Paging and the Pager Control

AN包括了另一个控件叫做DataPager,ListView用它来提供分页功能。DataPager控件提供分页导航,和任何实现了IPagableItemContainer接口的数据绑定控件协调,在AN中就是这个ListView控件。事实上,你会注意到,如果你启动了ListView的分页,这个控件仅仅就是在它的LayoutTemplate中插入一个DataPager控件。这个默认的分页markup如下

<asp:datapager ID="DataPager1" runat="server">
<Fields>
<asp:nextpreviouspagerfield ButtonType="Button" FirstPageText="First"
LastPageText="Last" NextPageText="Next" PreviousPageText="Previous"
ShowFirstPageButton="True" ShowLastPageButton="True" />
</Fields>
</asp:datapager>

这个markup显示,在DataPager中,生成了一个Fields集合,这个集合包含了一个NextPreviousPagerField对象。正如它的名字暗示的,使用NextPreviousPager对象,最后的结果就是渲染了Next和Previous按钮作为用户界面。这个DataPager控件包含了三种类型的Fields对象,NextPreviousPagerField,NumericPagerField对象,生成简单的数字分页列表。还有一个TemplatePagerField,允许你指定你自己定制的分页用户界面。每一个Field类型包含了一堆特征,你可以用来控制DataPager如何显示这个用户界面。另外,因为这个DataPager暴露了一个Fields集合而不是简单的Field特征,你可以显示好几个Field对象在一个DataPager控件中。
这个TemplatePagerField是一个唯一的Field对象,不包含任何界面,仅仅暴露一个模板,你可以用来完全的定制这个pager的用户界面。下面的例子演示了TemplatePagerField的使用。

<asp:DataPager ID="DataPager1" runat="server">
<Fields>
<asp:TemplatePagerField>
<PagerTemplate>
Page
<asp:Label ID="Label1" runat="server"
Text=
"<%# (Container.StartRowIndex/Container.PageSize)+1%>" />
of
<asp:Label ID="Label2" runat="server"
Text=
"<%# Container.TotalRowCount/Container.PageSize%>" />
</PagerTemplate>
</asp:TemplatePagerField>
</Fields>
</asp:DataPager>

注意这个例子使用了AN数据绑定来提供全部的页数量,分页大小等。这些数值都是由DataPager控件曝露的。
如果你想要在PagerTemplate使用自定制的导航控件,例如一个按钮来改变当前显示页,你要生成一个标准的点击事件处理。在这个事件处理中,你可以访问DataPager的StartRowIndex,TotalRowCount和PagerSize特征来计算这个新的StartRowIndex。
不像GridView提供的分页,因为DataPager是一个分开的控件,提供了你完全的自由度。这些你所看到的例子都将DataPager放置在了ListView中,但是这个控件可以被放置在Web form的任意地方。下面的例子你需要注意的就是PagedControlID特征。

<asp:DataPager ID="DataPager1" runat="server"
PagedControlID="ListView1">
<Fields>
<asp:NumericPagerField />
</Fields>
</asp:DataPager>

这个PageControlID特征允许你指定这个pager是和哪个控件合作的。

FormView

这个FormView和DetailsView的功能差不多,也是用来显示某个data source控件的单个data item,允许添加,编辑和删除数据。它的独特性在于它在自定制的模板中显示数据,让你能够更加自由的显示和编辑数据。这个FormView控件也包含了一个EditItemTemplate和InsertItemTemplate,允许你决定这个控件在进入编辑或者插入模式时如何显示。

OTHER DATA-BOUND CONTROLS

AN包含了许多其他的简单控件可以和data source绑定。这个部分看看三个最流行也最有用的控件和你如何在你的web应用中使用它们。

TreeView

TreeView显示层次化的结构数据。正因为此,它只能被XmlDataSource和SiteMapDataSource控件绑定。下面显示了一个SiteMap文件你可以使用在你的SiteMapDataSource控件中。

<siteMap>
<siteMapNode url="page3.aspx" title="Home" description="" roles="">
<siteMapNode url="page2.aspx"
title="Content" description="" roles="" />
<siteMapNode url="page4.aspx"
title="Links" description="" roles="" />
<siteMapNode url="page1.aspx"
title="Comments" description="" roles="" />
</siteMapNode>
</siteMap>

下面显示了你如何将一个TreeView控件绑定到一个SiteMapDataSource控件来生成你的网站的导航。

<div>
<asp:TreeView ID="TreeView1" Runat="server"
DataSourceID="SiteMapDataSource1" />
<asp:SiteMapDataSource ID="SiteMapDataSource1"
Runat="server" />
</div>

和TreeView控件一样,Menu控件也能够以垂直的pop-out风格菜单显示层次化的数据。和TreeView控件一样,它只能绑定到XmlDataSource和SiteMapDataSource控件。下面显示了你如何使用相同的SiteMap数据。

<asp:Menu ID="Menu1" Runat="server"
DataSourceID="SiteMapDataSource1" />
<asp:SiteMapDataSource ID="SiteMapDataSource1"
Runat="server" />

Chart

这是一个非常棒的控件。支持许多表类型。

INLINE DATA-BINDING SYNTAX

在AN中数据绑定的另一个特色是内嵌的数据绑定语法。AN1.0/1.1中的内嵌语法主要和模板控件例如DataList或者Repeater控件有关,甚至在那时候,按你所要的也是有点困难和令人困扰的。在AN1.0/1.1中,如果你需要使用内嵌的数据绑定,你可能需要类似于下面的这个过程。

<asp:Repeater ID="Repeater1" runat="server"
DataSourceID="SqlDataSource1">
<HeaderTemplate>
<table>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<%# Container.DataItem("ProductID")%><BR/>
<%# Container.DataItem("ProductName")%><BR/>
<%# DataBinder.Eval(
Container.DataItem, "UnitPrice", "{0:c}")%><br/>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>

在这个例子中你可以看到,你在使用一个Repeater控件显示一系列的employees。因为这个Repeater控件是一个模板化空间,你使用数据绑定在模板的合适位置输出具体的雇员数据。使用Eval方法也允许你提供格式化信息例如Date或者Currency。
在AN的后续版本中,内嵌数据绑定的概念基本上延续下来了,但是你使用一个更简单的语法和几个强大的绑定工具。

Data-Binding Syntax

AN涵盖了三种方法执行数据绑定。一种是继续使用现存的绑定方法,使用Container.DataItem语法

<%# Container.DataItem("Name") %>

这很好,因为这意味着你不需要改变你的网页。但是如果你在生成新的网页,你应该使用最简单的绑定形式,就是直接使用Eval方法

<%#Eval("Name")%>

你也可以使用Eval方法的格式化重载方法来继续格式信息

<%# Eval("HireDate","{0:mm dd yyyy}")%>

除了这些变化,AN包括了数据绑定的一种形式,所谓的two-way data binding。Two-way数据绑定允许你支持read和write操作。这是通过Bind方法。

<%# Bind("Name") %>

这个Bind方法应该使用在GridView,DetailsView或者FormView中,那些控件中数据源的自动更新都是实现的。
当和数据绑定语句打交道时,记住任何在<%# %>之间的都被作为表达式处理。这很重要因为当数据绑定的时候,它让你能够增加更多的功能。
例如,你可以附着更多的数据

<%# "Foo"+Eval("Name")%>

你甚至可以将评估的值传递给方法

<%# DoSomeProcess( Eval("Name") )%>

XML Data Binding

因为XML在应用中是这么流行,AN也有几种方法绑定到XML data sources。这些数据绑定表达式给了你和XML打交道的强大方法。另外,除了这些不同的方法名字,这些绑定方法和Eval和Bind方法工作机理是一样的。当你使用XmlDataSource控件时应该使用这些binders。第一个绑定格式使用XPathBinder类。

<% XPathBinder.Eval(Container.DataItem, "employees/employee/Name") %>

注意不像在Eval方法中指定一个列名,XPathBinder绑定了XPath查询的结果。和Eval表达一样,这个XPath数据绑定表达也有简洁格式

<% XPath("employees/employee/Name") %>

和Eval方法一样,XPath数据绑定表达支持对数据的格式化

<% XPath("employees/employee/HireDate","{0:mm dd yyyy}")%>

XPathBinder使用提供的XPath查询返回单个节点。如果你想要从XmlDataSource返回多个节点,你应该使用这个类的Select方法。这个方法返回匹配XPath查询的多个节点。

<% XPathBinder.Select(Container.DataItem,"employees/employee")%>

或者使用

<% XPathSelect("employees/employee")%>

##USING EXPRESSIONS AND EXPRESSION BUILDERS
Expressions是AN在运行的时候解析来返回值。当解析SqlDataSource控件时,AN自动地使用Expressions来获取数据库连接字符串,你在网页中已经看到过这些语句。一个ConnectionString expression如下所示

<asp:SqlDataSource ID="SqlDataSource1" Runat="server"
SelectCommand="SELECT * FROM [Customers]"
ConnectionString="<%$ ConnectionStrings:ConnectionString %>" />

当AN试图解析一个AN网页时,它在<%$ %>中寻找expressions。这个指示AN有一个表达式需要被解析。正如前面所示,它试图从web.config文件中定位NorthwindConnectionString的值。因为ConnectionStrings表达式前缀,所以AN知道这样做,这告诉了AN使用ConnectionStringsExpressionBuilder类来解析这个表达式。

<asp:Label runat="server" ID="Label1"
Text="<%$ AppSettings: LabelText %>" />
<asp:Label runat="server" ID="Label1"
Text="<%$ Resources: MyAppResources,Label1Text %>" />

除了使用expression builder类以外,你可以生成你自己的expressions。System.Web.Compilation.ExpressionBuilder基类提供了你几个方法你必须重载,如果你想要AN来正确地解析你的expression。下面显示了一个简单的自定制expression builder

[ExpressionPrefix("MyFirstCustomExpression")]
[ExpressionEditor("MyFirstCustomExpressionEditor")]
public class MyFirstCustomExpression : ExpressionBuilder
{
public override System.CodeDom.CodeExpression
GetCodeExpression(BoundPropertyEntry entry, object parsedD
ExpressionBuilderContext context)
{
return new CodeCastExpression("Int64",
new CodePrimitiveExpression(1000));
}
}

观察这个代码,注意几个事项,首先,你从ExpressionBuilder继承得到MyCustomExpression类。第二,你重载了GetCodeExpression方法。这个方法提供了你好几个参数,这些参数在你执行这个方法的时候很有用,它返回一个CodeExpression对象给AN,AN在运行的时候可以获取数据值。
这个BoundPropertyEntry参数入口这个表达式绑定了哪个特征。例如,Label的Text特征绑定在AppSettings和Resources表达式中。这个对象参数ParsedData包含了要被解析的数据。最后,这个ExpressionBuilderContext参数环境允许你引用这个表达式关联的虚拟路径或者模板控件。
在GetCodeExpression方法的体中,你生成了一个新的CodeCastExpression对象,这个对象从CodeExpression基类继承。这个CodeCastExpression告诉.NET生成合适的代码将一个类型投掷到另一个类型,在这种情况下,你是在将值1000投掷为一个Int64数据类型。当.NET执行这个CodeCastExpression时,它感觉就是在执行C#代码。注意从CodeExpression中可以继承很多个类让你能够生成你最后的代码表达式。
最后要注意的是添加到这个类的两个属性,ExpressionPrefix和ExpressionEditor属性帮助.NET决定这个类是否作为Expression使用,它们也帮助.NET需要解析expression时定位合适的Expression Builder。
当你生成了你的Expression builder 类后,你让.NET知道它。你通过在web.config的compliation节点中添加一个ExpressionBuilder。注意ExpressionPrefix的值添加到了这个ExpressionBuilder,帮助AN定位合适的Expression builder类。

<compilation debug="true" strict="false" explicit="true">
<expressionBuilders>
<add expressionPrefix="MyCustomExpression" type="MyCustomExpression"/>
</expressionBuilders>
</compilation>

GetCodeExpression不是仅有的可以重载的成员。其他几个有用的可以重载的方法包括ParseExpression,SupportsEvaluate和EvaluateExpression方法。
ParseExpression方法让你传递解析过的数据到GetCodeExpression方法中。在上面的例子中,CodeCastExpression值1000是硬编码的。然而,如果你想要开发者将这个值作为表达式的一部分,你只要使用ParseExpression方法。

[ExpressionPrefix("MySecondCustomExpression")]
[ExpressionEditor("MySecondCustomExpressionEditor")]
public class MySecondCustomExpression : ExpressionBuilder
{
public override System.CodeDom.CodeExpression
GetCodeExpression(BoundPropertyEntry entry, object parsed
ExpressionBuilderContext context)
{
return new CodeCastExpression("Int64",
new CodePrimitiveExpression(parsedData));
}
public override object ParseExpression
(string expression, Type propertyType,
ExpressionBuilderContext context)
{
return expression;
}
}

最后两个ExpressionBuilder重载是SupportsEvaluate和EvaluateExpression成员,如果你在没有编译的场景(在你的@Page指令中你指定compliationMode=”never”)运行你的网站。这个SupportEvaluate特征返回一个布尔值指示AN当这网页在无编译模式中这个表达式是否能被评估。如果返回True且这个网页执行在没有编译的模式,EvaluateExpression方法用来返回数据而不是GetCodeExpression方法。EvaluateExpression返回一个代表数据值得对象。

[ExpressionPrefix("MyThirdCustomExpression")]
[ExpressionEditor("MyThirdCustomExpressionEditor")]
public class MyThirdCustomExpression : ExpressionBuilder
{
public override System.CodeDom.CodeExpression
GetCodeExpression(BoundPropertyEntry entry, object parsedData,
ExpressionBuilderContext context)
{
return new CodeCastExpression("Int64",
new CodePrimitiveExpression(parsedData));
}
public override object ParseExpression
(string expression, Type propertyType,
ExpressionBuilderContext context)
{
return expression;
}
public override bool SupportsEvaluate
{
get
{
return true;
}
}
public override object EvaluateExpression(object target,
BoundPropertyEntry entry, object parsedData,
ExpressionBuilderContext context)
{
return parsedData;
}
}

从上面的例子看到,如果你想要重载EvaluateExpression方法,你只要从SupportEvaluate方法返回True。你其余需要做的就是从EvaluateExpression方法中返回一个对象。

SUMMARY

在这一章,你检查了AN中的数据绑定。数据源控件例如LinqDataSource,SqlDataSource和XmlDataSource使得从任何数量的数据源查询和显示数据变得简单。使用数据源控件的wizard,你知道生成强大的数据访问功能多么简单。
你检查了甚至是一个初学的开发者怎么样将数据源控件和GridView,ListView和DetailsView控件结合起来生成强大的数据执行应用,这只需要一点点代码量。
你看到AN包括了多少数据绑定控件,检查了AN工具箱中包含的控件特色,例如GridView,TreeView,ListView,FormView和Menu控件。
最后,你观察了内嵌的数据绑定语法怎么样被提高和加强,和XML有关的数据绑定表达。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值