几乎不需缩写代码的双向数据绑定是GridView控件最瞩目的功能,但其他相对于DataGrid的改进之处也不胜枚举,这些改进包括:可定义多个主键字段、新的列类型、样式和模板选项。

GridView的属性

  GridView的SortDirection和SortExpression属性用于指定当前列排序的方向和排序表达式。当用户单击某个列的标头时,这两个属性会由该控件的内建排序机制设置。整个排序引擎的开关取决于AllowSorting属性。EnableSortingAndPagingCallbacks属性用于指示该控件是否使用脚本回调功能进行分页和排序,有了该功能,我们不必执行到服务器的往返交互,也不必更改整个页面。

  该功能不需要ASP.NET AJAV Extensions 1.0,也不会使用ASP.NET 3.5中内建的AJAV引擎。它是类似于AJAX的、轻型的、嵌入框架的功能,能够通过专门的基于XMLHttpRequest对象的引擎,发起特定上下文的Web服务器调用。

  GridView控件中显示的每一行对应于一个特定类型的网格元素。在应用程序中,这些元素表面上是静态的,在该控件的生命周期各阶段基本保持原位。其他类型的元素呈现时间较短,只要需要完成某项操作时才会显示。这些动态元素包括编辑行、选定行及EmptyData元素。EmptyData用于定义网格被绑定到空数据源时在主体部分中显示的内容。

  GridView控件能够充分发挥新数据源对象模型的功能,在通过DataSourceID属性绑定到数据源控件时,其表现更为出色。GridView还支持传统的DataSource属性,但如果以这种方式绑定数据,该控件的某些功能(如内建的更新和分页功能)将无法发挥。

GridView控件的事件

  GridView控件除DataBind以外没有其他特别值得注意的方法。在许多情况下,我们不必调用GridView控件上的方法。当GridView绑定到数据源控件时,数据绑定过程会隐式地启动。

  RowCreated和RowDataBound事件与DataGrid的ItemCreated和ItemDataBound作用一样,只不过换了新名称。RowCommand事件也是这样,对应于DataGrid控件的ItemCommand事件。

简单数据绑定

  下面的代码演示了将数据绑定到GridView控件最简单的方法:

<asp:ObjectDataSource ID="MySource" runat="server" TypeName="Core35.DAL.Customers" SelectMethod="LoadAll">
</asp:ObjectDataSource>
<asp:GridView runat="server" id="grid" DataSourceID="MySource" />

  设置DatasourceID属性会触发绑定过程,该过程运行数据源的查询并填充网格的用户界面。

将数据绑定到GridView控件

  如果没有设置数据源属性,GridView控件不会呈现任何内容。

  如果GridView被绑定到空数据源,并且EmptyDataTemplate已被指定,呈现给用户的结果看起来也更友好:

<asp:gridview runat="server" datasourceid="MySource">
<emptydatatemplate>
<asp:label runat="server">
There's no data to show in this view.
</asp:label>
</emptydatatemplate>
</asp:gridview>

  GridView即可以有声明的列,也可以有自动生成的列。这种情况下,声明的列会在左侧显示。还应注意,自动生成的列不会被添加到Columns集合中。因此,若使用自动生成的列,Columns集合一般为空。

列的配置

  Columns属性是DataControlField对象的集合。我们即可以以声明方式定义列,也能以编程方式进行。以编程方式定义列的示例:

BoundField field = new BoundField();
field.DataField
= "companyname";
field.HeaderText
= "Company Name";
grid.ColumnFields.Add(field);

  使用声明方式定义列:

<columns>
<asp:boundfield datafield="customerid" headertext="ID" />
<asp:boundfield datafield="companyname" headertext="Company Name" />
</columns>

  下表列出了可在GridView控件中使用的列类型,所有列的类型都继承于DataControlField类:

  下表列出了所有列类型共有的主要属性:

绑定字段

  BoundField类代表数据绑定控件中以纯文本方式显示的字段。为指定要显示的数据字段,应将DataField设置为该字段的名称。还可通过DataFormatString为显示的值设置自定义的格式字符串。通过NullDdisplayText属性,我们可指定在出现null值时显示的替代文本。通过将ConvertEmptyStringToNull设置为true,我们能将空字符串按null值处理。

  通过BoundField的Visible属性可将该字段隐藏,而ReadOnly属性能阻止显示的值在编辑模式中被更改。为在标头和脚注栏中显示标题,可分别设置HeaderText和FooterText。我们还可设置HeaderImageUrl属性来选择替代文本的图像。

按钮字段

  按钮字段适合于在网格的列中添加可点击元素。当某个按钮被单击后,页面会回发,并引发RowCommand事件。

  我们可以选用不同类型的按钮--下压式按钮、链接按钮或图像按钮。图像按钮示例:

<asp:buttonfield buttontype="Image" CommandName="Add" ImageUrl="/core35/images/cart.gif" />

  要为按钮添加提示框(ToolTip),则需要处理RowCreated事件。

  如果没有额外的HtmlEncode="false"属性,BoundField类的DataFormatString是无法正常工作的。因为ASP.NET首先会对绑定字段的值进行HTML编码,然后才会应用格式字符串。这样,绑定的值不受指定格式字符串的影响。提前在该控件的生命周期中启用HTML编码是一种安全措施,旨在预防跨站点脚本攻击。

超链接字段

  可以两种方式设置URL:直接从绑定源获取,或通过编码的带有自定义查询字符串的URL。但在某些情况下,要访问的URL跟特定的应用程序有关,并没有存储在数据源中。这时,我们可以将DataNavigateUrlFormatString属性设置为硬编码的URL,并在查询字符串中添加一组参数:

<asp:HyperLinkField DataTextField="productname" HeaderText="Product"
DataNavigateUrlFields
="productid"
DataNavigateUrlFormatString
="productinfo.aspx?id={0}"
Target
="ProductView" />

  DataTextFormatString属性可以包含任何有效的标记,用{0}作为占位符,为数据绑定的值留出位置。

  为超链接所指向的页面选择目标时,我们还可以使用以下标准的目标值:_self、_parent、_new。

复选框字段

  CheckBoxField类型能够显示复选框,我们只能将其绑定到布尔类型的数据字段上。

图像字段

  这种列中的每个单元格包含一个<img>元素,因而底层的字段必须引用有效的URL。

模板字段

  下表列出了GridView支持的模板:

  我们可以在一个模板中绑定多个字段,但并不是所有的模板都支持数据绑定表达式。标头和脚注模板不支持数据绑定,若使用表达式,会引发异常。

  下面的代码为产品列定义了项目模板,该列包含两行,分别用于显示产品名称和包装信息。

<asp:templatefield headertext="Product">
<itemtemplate>
<b><%# Eval("productname") %></b>
<br />
available in
<%# Eval("quantityperunit") %>
</itemtemplate>
</asp:templatefield>

数据的分页

  如果GridView控件通过DataSource属性绑定到数据源(即不使用数据源控件),那么分页和其他操作(如排序和编辑)的整体行为几乎与DataGrid控件一致。在这种情况下,GridView会引发若干事件,以此来通知页面,由页面中的数据绑定代码进行下一步的操作并刷新。

无需编程的数据分页

  为启用GridView控件的分页功能,我们只需将AllowPaging属性设为true。这样,GridView会显示一个导航栏,并为响应用户单击导航按钮做好准备。

  当用户要查看另一页面而单击其中的按钮时,页面会进行回发,GridView会捕获相应的事件并在内部进行处理。使用GridView,我们不必编写PageIndexChanged事件的处理程序。GridView本身知道如何获取数据并显示被请求的新页面。示例代码:

<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AllowPaging="true" />

  GridView默认的用户界面不包含页码,添加页面标签只需要为PageIndexChanged事件编写代码:

protected void GridView1_PageIndexChanged(object sender, EventArgs e)
{
ShowPageIndex();
}
private void ShowPageIndex()
{
CurrentPage.Text
= (GridView1.PageIndex + 1).ToString();
}

  GridView控件本身并不知道如何获取新的页面,它仅仅会使绑定的数据源控件返回适合指定页面显示的记录。分页操作的细节最终由数据源控件决定。若绑定到SqlDataSource控件,分页需要将整个数据源绑定到网格控件上。若网格被绑定到ObjectDataSource控件,分页机制取决于该数据源控件连接到的业务对象的功能。

  先让我们看看绑定到SqlDataSource时的情况。若要进行分页,必须将DataSourceMode设置为DataSet(也是默认设置)。这表明整个数据集都会被获取,但只有当前页面指定的记录被显示出来。如果将EnableCaching设置为ture,启用SqlDataSource缓存功能,则整个数据集只会被下载一次,并在指定的时间段内保持在ASP.NET缓存中。但这会导致大量数据驻留内存。因此,除非多个用户共享少量数据,否则不推荐这种方法。

  如果要在数据库级进行分页,最好的方式是在存储过程中编写分页查询,并将存储过程绑定到SqlDataSource控件的SelectCommand属性上。这种情况下,应关闭数据源的缓存功能。

将分页负担转嫁给DAL

  我们需要基于层业务对象方法的特性来配置ObjectDataSource控件。一旦确定Select方法后,再添加一个带有两个额外参数的重载(这两个参数分别用于指示页面大小和当前页的起始索引)。在ObjectDataSource的声明中,我们将StartRowIndexParameterName和MaximumRowsParameterName属性分别设置为指示起始索引和页面大小的方法参数的名称。

  为使GridView能够通过ObjectDataSource控件对数据进行分页,我们还需将ObjectDataSource的EnablePaging属性设为true:

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
EnablePaging
="true"
TypeName
="Core35.DAL.Customers"
StartRowIndexParameterName
="firstRow"
MaximumRowsParameterName
="totalRow"
SelectMethod
="LoadByCountry">
<SelectParameters>
<asp:ControlParameter Name="country" ControlID="Countries"
PropertyName
="SelectedValue" />
</SelectParameters>
</asp:ObjectDataSource>

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false"
DataSourceID
="ObjectDataSource1"
AllowPaging
="true"
OnPageIndexChanged
="GridView1_PageIndexChanged">
<PagerSettings Mode="NextPreviousFirstLast" />
<Columns>
<asp:BoundField DataField="id" HeaderText="ID" />
<asp:BoundField DataField="companyname" HeaderText="Company" />
<asp:BoundField DataField="contactname" HeaderText="Contact" />
<Columns>
</asp:GridView>
<b>Page:</b><asp:Label runat="server" ID="CurrentPage" />

  上述代码中,我们只显式指定了选择方法工作的必要参数。与分页有关的两个参数由GridView来设置。指示页面大小写的参数会自动绑定到GridView控件的PageSize属性上,起始索引会通过页面的大小和当前页面的起始索引来自动推算。下面是LoadByCountry方法的原型:

public static CustomerCollection LoadByCountry(string country)
{
return LoadByCountry(country, -1, 0);
}

public static CustomerCollection LoadByCountry(string country, int totalRows, int firstRow)
{
//此处实现分页查询
}

导航栏的配置

  若将AllowPaging属性设为true,GridView将显示导航栏。通过<PagerSettings>和<PagerStyle>标签,或与二者等价的属性,我们能在很大程度上控制导航栏的特性。PagerSettings的Mode属性用于设置导航栏的用户界面。下表列出了可用的模式:

  根据GridView的大小,GridView的第一行与最后一行间的距离可能超出屏幕实际的大小。为使用户能更容易导航,而不必频繁滑动滚动条,我们可使网格分别在顶端和底端显示导航栏。为此,只要设置<PagerSettings>元素的Position属性即可。

<PagerSetting Position="TopAndBottom" />

数据的排序

  GridView没有实现排序算法,而是依赖于数据源控件来提供已排序的数据。

不需要编程的数据排序

  为启用GridView的排序功能,我们应将AllowSorting属性设置为true。如果GridView的排序功能被启用,便可使各列的标头文本以超链接的形式呈现。

  通过SortExpression属性,我们能使每列与一个排序表达式相关联。排序表达式是一个列名的序列,两列名间由逗号分隔,每个列名后还可以跟DESC或ASC限定词来对排序做进一步的说明。

  示例代码:

<asp:GridView runat="server" id="MyGridView" DataSourceID="MySource
AllowSorting="
true" AutoGenerateColumns="false">
<Columns>
<asp:BoundField datafield="productname" headertext="Product"
sortexpression
="productname" />
<asp:BoundField datafield="quantityperunit" headertext="Packaging" />
</Columns>
</asp:GridView>

  以SqlDataSource控件为例:默认情况下,SqlDataSource控件会获取所有数据填入DataSet对象,然后用DataView来包装这些数据,并调用DataView的Sort方法。这种方式虽然可行,但占用的是Web服务器的内存。如果分页和排序针对同一组数据,且数据量较小,那么结合缓存功能使用该方法还比较适宜。

  可否事先在数据库服务器上对数据进行排序?为此,我们首先应将SqlDataSource的DataSourceMode属性设置为DataReader。如将其设为DataSet,排序仍会在内存中进行。第二步需要我们编写获取数据的存储过程,为获取已排序数据,我们还要将数据源控件的SortParameterName设置为存储过程中指示排序表达式的参数名。显然,我们需要该存储过程动态地构建命令文本,使用查询语句带有ORDER BY子句。下面对NorthWind的CustOrderHist存储过程进行了修改,使其能按需对结果进行排序:

Create Procedure CustOrderHistSorted
@CustomerID nchar(5), @SortedBy varchar(20)='total'
AS
Set QUOTED_IDENTIFIER OFF
IF @SortedBy = ''
Begin
Set @SortedBy = 'total'
End

Exec(
'Select ProductName, Total=Sum(Quantity) ' +
'From Products P, [Order Details] OD, Orders O, Customer C' +
'Where C.CustomerID = '' ' + @CustomerID + ' '' ' +
'And C.CustomerID = O.CustomerID And O.OrderID = OD.OrderID ' +
'And OD.ProductID = P.ProductID Group By ProductName ' +
'Order By ' + @SortedBy)
Go

  这样,GridView便能显示已排序数据列,排序的负担被转嫁给了数据库。

<asp:SqlDataSource ID="SqlDataSource1" runat="server" DataSourceMode="DataReader"
ConnectionString
='<%$ ConnectionStrings:NWind %>'
SortParameterName="SortedBy"
SelectCommand="CustOrderHistSorted"
SelectCommandType="StoreProcedure">
<SelectParameters>
<asp:ControlParameter ControlID="CustList" Name="CustomerID"
PropertyName
="SelectedValue" />
</SelectParameter>
</asp:SqlDataSource>

  像这样在数据库上对数据进行排序与缓存功能是不兼容的,我们需要将EnableCaching设置为false,否则会抛出异常。

将排序操作负担转嫁给DAL

  使用ObjectDataSource控件如何排序?下面对之前讨论分页时使用的LoadByCountry方法稍做修改,添加一个排序表达式参数:

public static CustomerCollection LoadByCountry(string country, int totalRows, int firstRow, string sortExpression)
{
//此处实现分页查询
}
  这时,我们应将ObjectDataSource控件的SortParameterName设置为用于指示该排序方式的参数名称:
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" EnablePaging="true"
TypeName
="Core35.DAL.Customers"
SortParameterName
="sortExpression"
StartRowIndexParameterName
="firstRow"
MaximumRowsParameterName
="totalRows"
SelectMethod
="LoadByCountry">
<SelectParameters>
...
</SelectParameters>
</asp:ObjectDataSource>

对用户进行反馈

  GridView控件不会自动向输出中添加指示排序方向的可视元素,为完善排序,我们需要自行编写代码:

<script runat="server">
void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
//如果当前行是标头,则另行处理
if(e.Row.RowType == DataControlRowType.Header)
AddGlyph(sender
as GridView, e.Row);
}

void AddGlyph(GridView grid, GridViewRow item)
{
//创建Label控件,并禁用其主题
Label glyph = new Label();
glyph.EnableTheming
= false;

//设置Label控件属性
glyph.Font.Name = "webdings";
glyph.Font.Size
= FontUnit.Small;
//" 5"和" 6"表示正序或倒序排列的小三角标

glyph.Text
= (grid.SortDirection == SortDirection.Ascending ? " 5" : " 6");

//查找当前排序的列
for(int i = 0; i < grid.Columns.Count; i++)
{
//比较排序表达式来确定是否当前排序列
string colExpr = grid.Columns[i].SortExpression;
//如果找到当前排序列,将Label控件添加在上面
if(colExpr != "" && colExpr == grid.SortExpression)
item.Cells[i].Controls.Add(glyph);
}
}
</script>

通过回调进行分页和排序

  ASP.NET中有脚本回调机制使GridView无需将整个页面回发就能进行分页和排序。要打开此功能,只需将EnableSortingAndPagingCallbacks属性设为true即可。

数据的编辑

  GridView控件提供了完整的数据编辑解决方案。如果数据源支持更新,那么该控件会自动执行提交更改的操作。数据源控件会通过布尔类型的CanUpdate属性指示自身是否有更新功能。

posted on 2011-04-22 13:05  辛勤的代码工  阅读(1442)  评论(1编辑  收藏  举报