.NET Web高级开发
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2.3 GridView和DetailsView控件

DataGrid是ASP.NET中最受欢迎的控件之一,但在某些方面,它也成为自己成功的牺牲品:如此丰富的功能,以至于让ASP.NET开发人员不满足于此,而是希望它能提供更多功能。DataGrid控件在ASP.NET 2.0中并没有发生太大变化,只是添加了两个分别名为GridView和DetailsView的新控件,它们提供了通常要求DataGrid控件所具有的功能,并且还加入了一些属于它们自己的新功能。

GridView呈现出HTML表的方式与DataGrid一样,但与DataGrid不同的是,GridView可以完全依靠自己来分页和排序。GridView还支持比DataGrid种类更为丰富的列类型(在GridView用语中称为字段类型),并且它们具有更为智能的默认呈现行为,能够自动呈现Boolean值(例如,通过复选框)。GridView也可以容易地与DetailsView搭配使用,以创建主-从视图。GridView控件的主要缺陷是:像DataGrid一样,它通过将信息传回到服务器来完成它该做的大部分工作。

具体来说GridView可以完成以下一些操作:

· 通过数据源控件自动绑定和显示数据。

· 通过数据源控件对数据进行选择、排序、分页、编辑和删除。

· 通过自定义GridView控件来改变外观和行为:指定自定义列和样式。

· 利用模板创建自定义用户界面(UI)元素。

· 通过事件将自己的代码添加到控件的功能中去。

1.通过数据源控件自动绑定和显示数据

GridView控件提供了两种方式用于绑定到数据:要么使用DataSourceID属性进行数据绑定,此方法能够将GridView控件绑定到数据源控件,同时这种方式比较常见也建议使用,因为它允许GridView控件利用数据源控件的功能并提供了内置的排序、分页和更新功能;要么使用DataSource属性进行数据绑定,通过这种方式能够绑定到包括ADO.NET数据集和数据读取器在内的各种对象。此方法需要为所有附加功能(如排序、分页和更新)编写代码。当使用DataSourceID属性绑定到数据源时,GridView控件支持双向数据绑定。除可以使该控件显示返回的数据之外,还可以使它自动支持对绑定数据的更新和删除操作。

2.通过数据源控件对数据进行选择、排序、分页、编辑和删除

GridView控件有一个内置分页功能,可支持基本的分页功能。读者可以使用默认分页功能或创建自定义的分页效果。GridView控件支持对其数据源中的项进行分页。将AllowPaging属性设置为true就可以启用分页。GridView控件可以通过多种方式支持分页:如果将GridView控件绑定到在界面级别支持分页功能的数据源控件,则GridView控件将直接利用这一功能。在界面级别分页意味着GridView控件仅从数据源请求呈现当前页所需的记录数。请求的记录数可能会根据其他因素变化,例如数据源是否支持获取总记录数,由PageSize属性指定的每页的记录数,以及在将PageButtonCount属性设置为Numeric时要显示的页导航按钮的数目。而实际上在.NET Framework所包含的数据源控件中,只有ObjectDataSource控件在界面级别支持分页;如果将GridView控件绑定到不直接支持分页功能的数据源控件,或者如果通过DataSource属性利用代码将GridView控件绑定到一个数据结构,则GridView控件将按照先从源获取所有数据记录来进行分页,且仅显示当前页的记录,然后丢弃剩余的记录。但是只有在GridView控件的数据源返回一个实现ICollection接口的集合(包括数据集)时,才支持这种分页方式。

需要注意的是,如果数据源不直接支持分页且未能实现ICollection接口,则GridView控件将无法进行分页。例如读者正在使用SqlDataSource控件,并将其DataSourceMode属性设置为DataReader,则GridView控件就无法实现分页。

对于自定义分页设置和用户界面,读者可以通过多种方式自定义GridView控件的分页用户界面。可以通过使用PageSize属性来设置页的大小(即每次显示的项数)。还可以通过设置PageIndex属性来设置GridView控件的当前页。可以使用PagerSettings属性或通过提供页导航模板来指定更多的自定义行为。分页模式将AllowPaging属性设置为true时,PagerSettings属性则允许自定义由GridView控件自动生成的分页用户界面的外观。GridView控件可显示允许向前和向后导航的方向控件,以及允许用户移动到特定页的数字控件。

对于分页事件,当GridView控件移动到新的数据页时,该控件会引发两个事件:PageIndexChanging事件在GridView控件执行分页操作之前发生;PageIndexChanged事件在新的数据页返回到GridView控件之后发生。如果需要可以使用PageIndexChanging事件取消分页操作或在GridView控件请求新的数据页之前执行某项任务,也可以使用PageIndexChanged事件在用户移动到另一个数据页之后执行某项任务。在默认情况下,GridView控件在只读模式下显示数据。但是该控件还支持一种编辑模式,在该模式下控件显示一个包含可编辑控件(如TextBox或CheckBox控件)的行。还可以对GridView控件进行配置以显示一个Delete按钮,用户可单击该按钮来删除数据源中相应的记录。同时GridView控件支持在不需要任何编程的情况下通过单个列排序。通过使用排序事件以及提供排序表达式就可以进一步自定义GridView控件的排序功能。

3.实现GridView批量删除、自定义分页、定位页码

下面以一个完整的自定义分页程序演示前面的部分理论,其代码如下:

  <table height="20" border="1" align="center" cellpadding="0" cellspacing="0"
style="width: 97%">
      <tr>
      <td align="center" style="width: 24%">
      <asp:CheckBox ID="cbAll" runat="server" AutoPostBack="True" OnChecked
Changed="cbAll_CheckedChanged" Text="Select All /UnSelect All" />
      </td>
      <td align="center" style="width: 16%">ProductName</td>
      <td width="39%" align="center">UnitPrice</td>
      <td width="21%" align="center">IsDiscontinued</td>
      </tr>
      </table>
      <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
      BackColor="White" BorderColor="#E7E7FF" BorderWidth="1px" CellPadding="3"
      DataKeyNames="ProductID" HorizontalAlign="Center"
      Width="97%"  BorderStyle="None"  ShowHeader="False"  AllowPaging="True"
OnDataBound="GridView1_DataBound" GridLines="Horizontal">
      <FooterStyle BackColor="#B5C7DE" ForeColor="#4A3C8C" />
      <Columns>
      <asp:TemplateField >
      <ItemStyle HorizontalAlign="Center" />
      <ItemTemplate>
      <asp:CheckBox ID="checkAll" runat=server />
      </ItemTemplate>
      </asp:TemplateField>
      <asp:BoundField DataField="ProductName" HeaderText="ProductName">
      <ItemStyle Width="16%" />
      </asp:BoundField>
      <asp:HyperLinkField  DataNavigateUrlFields="ProductID"  DataNavigateUrl
FormatString="Test.aspx?id={0}"
      DataTextField="UnitPrice" HeaderText="UnitPrice" >
<ItemStyle Width="39%" />
</asp:HyperLinkField>
<asp:BoundField DataField="Discontinued" HeaderText="Discontinued">
<ItemStyle Width="21%" />
</asp:BoundField>
</Columns>
<PagerTemplate>
</PagerTemplate>
<SelectedRowStyle BackColor="#738A9C" ForeColor="#F7F7F7" Font-Bold= "True" />
<PagerStyle  BackColor="#E7E7FF"  ForeColor="#4A3C8C"  HorizontalAlign=
"Right" />
<HeaderStyle BackColor="#4A3C8C" Font-Bold="True" ForeColor="#F7F7F7" />
<RowStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" />
<AlternatingRowStyle BackColor="#F7F7F7" />
</asp:GridView>
<table height="20" border="1" cellpadding="0" cellspacing="0" bordercolor
light="#FFFFFF" bordercolordark="#E6E6E6" bgcolor="#FFFFFF" style="width: 99%">
<tr><td>
<asp:Button ID="Button1" runat="server" Text="全选" OnClick="Button1_
Click" /> 
<asp:Button ID="Button2" runat="server" Text="删除" OnClick="Button2_
Click" /></td>
<td align=right>
<asp:LinkButton ID="lnkbtnFrist" runat="server" OnClick="lnkbtnFrist_
Click">首页</asp:LinkButton>
<asp:LinkButton   ID="lnkbtnPre"   runat="server"   OnClick="lnkbtnPre_
Click">上一页</asp:LinkButton>
<asp:Label ID="lblCurrentPage" runat="server"></asp:Label>
<asp:LinkButton  ID="lnkbtnNext"  runat="server"  OnClick="lnkbtnNext_
Click">下一页</asp:LinkButton>
<asp:LinkButton  ID="lnkbtnLast"  runat="server"  OnClick="lnkbtnLast_
Click">尾页</asp:LinkButton>
跳转到第<asp:DropDownList ID="ddlCurrentPage" runat="server" AutoPostBack
="True" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged">
</asp:DropDownList>页</td>
</tr></table>

最终的显示效果如图1-12所示。

图1-12

后台代码如下部分:

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
DataBinds();
}
}
//数据绑定
void DataBinds()
{
this.GridView1.DataSource = GetDataFromDataBase().Tables[0].Default View;
this.GridView1.DataBind();
this.ddlCurrentPage.Items.Clear();
for (int i = 1; i <= this.GridView1.PageCount; i++)
{
this.ddlCurrentPage.Items.Add(i.ToString());
}
this.ddlCurrentPage.SelectedIndex = this.GridView1.PageIndex;
}
//全选操作
protected void Button1_Click(object sender, EventArgs e)

{

      foreach (GridViewRow row in GridView1.Rows)
      {
          ((CheckBox)row.Cells[0].FindControl("checkAll")).Checked = true;
      }
    }
    //删除所选
    protected void Button2_Click(object sender, EventArgs e)
    {
      for (int rowindex = 0; rowindex < this.GridView1.Rows.Count; rowindex++)
      {
          if
(((CheckBox)this.GridView1.Rows[rowindex].Cells[0].FindControl("checkAll")).Checked == true)
          {
        int   id   =   Convert.ToInt32(this.GridView1.DataKeys[rowindex].
Value);
        //DeleteProduct(id);
          }
      }
      DataBinds();
}
//索引事件
    protected void GridView1_PageIndexChanging(object sender, GridView PageEvent
Args e)
    {
      this.GridView1.PageIndex = e.NewPageIndex;
      DataBinds();
}
//改变CheckBox的状态
    protected void cbAll_CheckedChanged(object sender, EventArgs e)
    {
      if (this.cbAll.Checked == true)
      {
          foreach (GridViewRow row in GridView1.Rows)
          {
        ((CheckBox)row.Cells[0].FindControl("checkAll")).Checked = true;
          }
      }
else
{
foreach (GridViewRow row in GridView1.Rows)
{
((CheckBox)row.Cells[0].FindControl("checkAll")).Checked = false;
}
}
}
//自定义导航到具体的页
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
this.GridView1.PageIndex = this.ddlCurrentPage.SelectedIndex;
DataBinds();
}
//首页
protected void lnkbtnFrist_Click(object sender, EventArgs e)
{
this.GridView1.PageIndex = 0;
DataBinds();
}
//前一页
protected void lnkbtnPre_Click(object sender, EventArgs e)
{
if (this.GridView1.PageIndex > 0)
{
this.GridView1.PageIndex = this.GridView1.PageIndex - 1;
DataBinds();
}
}
//下一页
protected void lnkbtnNext_Click(object sender, EventArgs e)
{
if (this.GridView1.PageIndex < this.GridView1.PageCount)
{
this.GridView1.PageIndex = this.GridView1.PageIndex + 1;
DataBinds();
}
}
//最后一页
    protected void lnkbtnLast_Click(object sender, EventArgs e)
    {
      this.GridView1.PageIndex = this.GridView1.PageCount;
      DataBinds();
    }
    protected void GridView1_DataBound(object sender, EventArgs e)
    {
      this.lblCurrentPage.Text = string.Format("当前第{0}页/总共{1}页",
      this. GridView1.PageIndex + 1, this.GridView1.PageCount);
    }
//数据源
    public DataSet GetDataFromDataBase()
    {
      string getConnetionString = string.Empty;
      getConnetionString Configuration.ConfigurationManager.AppSettings
      ["ConnectionString"].ToString();
      string sql = "select ProductID,ProductName,UnitPrice, Discontinued from
dbo.Products";
      SqlConnection conn = new SqlConnection();
      conn.ConnectionString = getConnetionString;
      SqlDataAdapter  adp = new SqlDataAdapter(sql, conn);
      DataSet ds = new DataSet();
      adp.Fill(ds);
      return ds;
}

数据库配置文件如下:

<appSettings>
    <add  key="ConnectionString"  value="Server=;  user  id=sa;  pwd=123456;
database= Northwind"/>
</appSettings>

最后整个程序显示效果如图1-13所示。

图1-13

对于DetailsView控件,它的作用在于在表格中显示数据源的单个记录,此表格中每个数据行表示记录中的一个字段,可以从它的关联数据源中一次显示、编辑、插入或删除一条记录。默认情况下,会将记录的每个字段显示在它自己的一行内。DetailsView控件通常用于更新和插入新记录,并且通常在主/详细方案中使用,在这些方案中,主控件(最常见的是与GridView控件一起使用)的选中记录决定要在DetailsView控件中显示的记录。即使DetailsView控件的数据源公开了多条记录,该控件一次也仅显示一条数据记录。此控件依赖于数据源控件的功能执行诸如更新、插入和删除记录等任务。但是此控件不支持排序。可以自动对其关联数据源中的数据进行分页,但前提是数据由支持ICollection接口的对象表示或基础数据源支持分页。DetailsView控件提供用于在数据记录之间导航的用户界面。若要启用分页行为,则需要将AllowPaging属性设置为true。从关联的数据源选择特定的记录时,可以通过分页到该记录进行选择。由该控件显示的记录是当前选择的记录。

和GridView控件一样,DetailsView控件也有两种数据绑定方式:使用DataSourceID属性进行数据绑定;使用DataSource属性进行数据绑定。但要注意的是,当使用DataSourceID属性绑定到数据源时,DetailsView控件支持双向数据绑定。除可以使该控件显示数据之外,还可以使它自动支持对绑定数据的插入、更新和删除操作。此时若要使DetailsView控件支持编辑操作,绑定数据源必须支持对数据的更新操作,需要同时满足才能达到此目的。

1.如何在DetailsView控件中进行分页

如果DetailsView控件被绑定到某个数据源控件或任何实现了ICollection接口的数据结构(包括数据集),则此控件将从数据源获取所有记录,显示当前页的记录,并丢弃其余的记录。当用户移到另一页时,控件会重复此过程,显示另一条记录。对于有些数据源(如ObjectDataSource控件)提供更高级的分页功能。DetailsView控件在分页时可以利用这些数据源的更加高级的功能,从而获得更好的性能和更大的灵活性。同样如果数据源未实现ICollection接口,DetailsView控件将无法分页。例如如果使用SqlDataSource控件,并将其DataSourceMode属性设置为DataReader,则DetailsView控件是无法实现分页功能的。

下面演示一个配置为提供分页的DetailsView控件:

<h3>DetailsView Example Demo</h3>
      <table cellspacing="10">
        <tr>
          <td valign="top">
            <asp:DetailsView ID="EmployeesDetailsView"
        DataSourceID="EmployeesSqlDataSource"
        AutoGenerateRows="False"
        AllowPaging="True"
        DataKeyNames="EmployeeID"
        runat="server">
        <HeaderStyle forecolor="White" backcolor="Blue" />
        <Fields>
                <asp:BoundField Datafield="EmployeeID" HeaderText="Employee ID"
ReadOnly="True"/>
                <asp:BoundField Datafield="Title"  HeaderText="Title"/>
                <asp:BoundField Datafield="BirthDate"  HeaderText="BirthDay"/>
        </Fields>
        <PagerSettings Mode="NextPreviousFirstLast"
                            FirstPageText="<<"
                            LastPageText=">>"
                            PageButtonCount="1"
                            Position="Top"/>
            </asp:DetailsView>
          </td>
        </tr>
      </table>
      <asp:SqlDataSource ID="EmployeesSqlDataSource"
        SelectCommand="SELECT [EmployeeID], [Title], [BirthDate] FROM [Employees]"
        connectionstring="<%$ ConnectionStrings:NorthwindConnectionString %>"
        RunAt="server"/>

2.使用DetailsView控件修改数据

通过将AutoGenerateEditButton、AutoGenerateInsertButton和AutoGenerateDeleteButton属性中的一个或多个设置为true,就可以启用DetailsView控件的内置编辑功能。DetailsView控件将自动添加此功能,使用户能够编辑或删除当前绑定的记录以及插入新记录,但前提是DetailsView控件的数据源支持编辑。DetailsView控件需要提供一个用户界面,使用户能够修改绑定记录的内容。通常在一个可编辑视图中会显示一个附加行,其中包含“编辑”、“插入”和“删除”命令按钮。默认情况下,这一行会添加到DetailsView控件的底部。当用户单击某个命令按钮时,DetailsView控件会重新显示该行,并在其中显示可让用户修改该行内容的控件。编辑按钮将被替换为可让用户保存更改或取消编辑行的按钮。

DetailsView控件使用文本框显示BoundField中的数据,以及那些在将AutoGenerateRows属性设置为true时自动显示的数据。布尔型数据使用复选框显示。通过使用TemplateField,当然也可以自定义在编辑模式中显示的输入控件。当DetailsView控件执行插入操作时,将使用Values字典集合传递要插入到数据源中的值。对于更新或删除操作,DetailsView控件使用这三个字典集合将值传递到数据源:Keys字典、NewValues字典和OldValues字典。读者可以使用传递到插入、更新或删除事件的事件参数访问各个字典,这些事件由DetailsView控件引发。

Keys字典中包含一些字段的名称和值,这些字段唯一地标识了包含要更新或删除的记录,此外该字典总是包含编辑记录之前键字段的原始值。若要指定哪些字段放置在Keys字典中,可将DataKeyNames属性设置为表示数据的主键的字段名称列表,各字段名称之间用逗号分隔。Keys集合中会自动填充与在DataKeyNames属性中指定的字段关联的值。Values和NewValues字典分别包含所插入或编辑的记录中的输入控件的当前值。OldValues字典包含除键字段以外的任何字段的原始值,键字段包含在Keys字典中。键字段的新值包含在NewValues字典中。

下面演示一个使用GridView控件和DetailsView控件显示数据,并将DetailsView控件配置为允许修改数据:

<h3>Northwind Employees</h3>
      <table cellspacing="10">
        <tr>
          <td valign="top">
            <asp:DropDownList ID="EmployeesDropDownList"
        DataSourceID="EmployeesSqlDataSource"
        DataValueField="EmployeeID"
        DataTextField="FullName"
        AutoPostBack="True"
        Size="10"
        OnSelectedIndexChanged="EmployeesDropDownList_OnSelectedIndex
                          Changed"
        RunAt="Server" />
          </td>
          <td valign="top">
            <asp:DetailsView ID="EmployeeDetailsView"
        DataSourceID="EmployeeDetailsSqlDataSource"
        AutoGenerateRows="False"
        AutoGenerateInsertbutton="True"
        AutoGenerateEditbutton="True"
        AutoGenerateDeletebutton="True"
        DataKeyNames="EmployeeID"
        OnItemUpdated="EmployeeDetailsView_ItemUpdated"
        OnItemDeleted="EmployeeDetailsView_ItemDeleted"
        RunAt="server">
        <HeaderStyle backcolor="Navy"
                forecolor="White"/>
        <RowStyle backcolor="White"/>
        <AlternatingRowStyle backcolor="LightGray"/>
        <EditRowStyle backcolor="LightCyan"/>
        <Fields>
        <asp:BoundField DataField="EmployeeID" HeaderText="Employee ID"
        InsertVisible="False" ReadOnly="True"/>
                <asp:BoundField DataField="FirstName"  HeaderText="First Name"/>
                <asp:BoundField DataField="LastName"   HeaderText="Last Name"/>
                <asp:BoundField DataField="Address"   HeaderText="Address"/>
                <asp:BoundField DataField="City"      HeaderText="City"/>
                <asp:BoundField DataField="Region"    HeaderText="Region"/>
                <asp:BoundField DataField="PostalCode" HeaderText="Postal Code"/>
        </Fields>
            </asp:DetailsView>
          </td>
        </tr>
      </table>
      <asp:SqlDataSource ID="EmployeesSqlDataSource"
        SelectCommand="SELECT EmployeeID, LastName + ', ' + FirstName AS FullName
                FROM Employees"
        Connectionstring="<%$ ConnectionStrings:NorthwindConnectionString %>"
        RunAt="server">
      </asp:SqlDataSource>
      <asp:SqlDataSource ID="EmployeeDetailsSqlDataSource"
        SelectCommand="SELECT EmployeeID, LastName, FirstName, Address, City,
Region, PostalCode FROM Employees WHERE EmployeeID = @EmpID"
        InsertCommand="INSERT INTO Employees(LastName, FirstName, Address, City,
                Region, PostalCode) VALUES (@LastName, @FirstName,
                @Address, @City, @Region, @Postal Code);
        SELECT @EmpID = SCOPE_IDENTITY()"
        UpdateCommand="UPDATE  Employees  SET  LastName=@LastName,  FirstName=
@FirstName,  Address=@Address,City=@City, Region=@Region, PostalCode=@PostalCode
        WHERE EmployeeID=@EmployeeID"
        DeleteCommand="DELETE Employees WHERE EmployeeID=@EmployeeID"
        ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
        OnInserted="EmployeeDetailsSqlDataSource_OnInserted"
        RunAt="server">
        <SelectParameters>
          <asp:ControlParameter  ControlID="EmployeesDropDownList"  Property
Name="SelectedValue" Name="EmpID" Type="Int32" DefaultValue="0" />
        </SelectParameters>
        <InsertParameters>
          <asp:Parameter Name="LastName"   Type="String" />
          <asp:Parameter Name="FirstName"  Type="String" />
          <asp:Parameter Name="Address"   Type="String" />
          <asp:Parameter Name="City"      Type="String" />
          <asp:Parameter Name="Region"    Type="String" />
          <asp:Parameter Name="PostalCode" Type="String" />
          <asp:Parameter Name="EmpID" />
        </InsertParameters>
        <UpdateParameters>
          <asp:Parameter Name="LastName"   Type="String" />
          <asp:Parameter Name="FirstName"  Type="String" />
          <asp:Parameter Name="Address"   Type="String" />
          <asp:Parameter Name="City"      Type="String" />
          <asp:Parameter Name="Region"    Type="String" />
          <asp:Parameter Name="PostalCode" Type="String" />
          <asp:Parameter Name="EmployeeID" Type="Int32" DefaultValue="0" />
        </UpdateParameters>
        <DeleteParameters>
          <asp:Parameter Name="EmployeeID" Type="Int32" DefaultValue="0" />
        </DeleteParameters>
      </asp:SqlDataSource>

后台代码如下:

public   void   EmployeesDropDownList_OnSelectedIndexChanged(Object   sender,
EventArgs e)
    {
      EmployeeDetailsView.DataBind();
    }
    public  void  EmployeeDetailsView_ItemUpdated(Object  sender,  DetailsView
UpdatedEventArgs e)
    {
      EmployeesDropDownList.DataBind();
      EmployeesDropDownList.SelectedValue = e.Keys["EmployeeID"].ToString();
      EmployeeDetailsView.DataBind();
    }
    public  void  EmployeeDetailsView_ItemDeleted(Object  sender,  DetailsView
DeletedEventArgs e)
    {
      EmployeesDropDownList.DataBind();
    }
    public void EmployeeDetailsSqlDataSource_OnInserted(Object sender, SqlData
SourceStatusEventArgs e)
    {
      System.Data.Common.DbCommand command = e.Command;
      EmployeesDropDownList.DataBind();
      EmployeesDropDownList.SelectedValue =
        command.Parameters["@EmpID"].Value.ToString();
      EmployeeDetailsView.DataBind();
}

运行效果如图1-14所示。

图1-14

要注意GridView和DetailsView控件中用于定义字段类型的元素。这些元素实际上等效于DataGrid控件中的元素。表1-4列出了受支持的字段类型。特别重要的是ImageField和DropDownListField,它们都可以有效地削减目前开发人员为在DataGrid中包含图像和数据绑定下拉列表而编写的大部分代码。

表1-4 GridView和DetailsView字段类型