登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

hurt0759的个人主页

人生常态--跋涉.人生暂态--歇息.

 
 
 

日志

 
 

asp.net DataGrid  

2009-08-26 14:43:28|  分类: work |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

 DataGrid控件

 

    inline:一种处理数据的方法,即这些数据事先不必经过编辑或分类排序,按照它们到达 的顺序,按次序排队处理。

    可以通过DataGrid方便地来检索XML数据。考察DataGrid的另一项有用的特点—inline编辑(inline editing)

显示数据(在本例中使用的数据库是GrocerToGo(一种Access 2000的数据库))

    将在DataGrid里显示Products表里的如下几个列:ProductID, UnitPrice, ProductName,以及ProductDescription,为了使DataGrid看起来比较美观,还包含了一些界面格式代码,下面的代码将Products表的数据显示在DataGrid:

<%@ Import Namespace="System.Data" %>

<%@ Import Namespace="System.Data.OleDb" %>

<script runat="server">

Sub Page_Load(sender as Object, e as EventArgs)//VB代码

   If Not Page.IsPostBack //此处如果修改为每次登录页面时都调用BindData(),编辑的数值就不会保存在数据库里。

      BindData()

   End If

End Sub

Sub BindData()

    '1. Create a connection

    Const strConnStr as String = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

                                        "Data Source=C:\GrocerToGo.mdb"

    Dim objConn as New OleDbConnection(strConnStr)

    objConn.Open()

     

    '2. Create a command object for the query

    Const strSQL as String = "SELECT * FROM Products"

    Dim objCmd as New OleDbCommand(strSQL, objConn)

     

    '3. Create/Populate the DataReader

    Dim objDR as OleDbDataReader

    objDR = objCmd.ExecuteReader()   

     

    dgProducts.DataSource = objDR

    dgProducts.DataBind()  

End Sub

</script>

<asp:DataGrid id="dgProducts" runat="server"

    AutoGenerateColumns="False" CellPadding="4"

    HeaderStyle-BackColor="Black"

    HeaderStyle-ForeColor="White"

    HeaderStyle-HorizontalAlign="Center"

    HeaderStyle-Font-Bold="True">

       

    <Columns>

        <asp:BoundColumn HeaderText="Product ID" DataField="ProductID" />

        <asp:BoundColumn HeaderText="Price" DataField="UnitPrice"

                ItemStyle-HorizontalAlign="Right"

                DataFormatString="{0:$#,###.##}" />

        <asp:BoundColumn HeaderText="Name" DataField="ProductName" />

        <asp:BoundColumn HeaderText="Description"

                DataField="ProductDescription" />                       

    </Columns>       

</asp:DataGrid>

    BindData()函数读取Products表的数据到一个DataReader,然后绑定到一个名为dgProducts的DataGrid。

允许最终用户编辑DataGrid里的某一行

    为了让用户编辑DataGrid里的某一行,需要一些途径供用户选择他想编辑的行。可以使用ButtonColumn控件为DataGrid里的每一行提供一个可点击的按钮。这样也可以达到编辑的目的,但看起来会有一点凌乱。DataGrid提供了EditCommandColumn控件以实现该功能。其在DataGrid的每一行显示一个"Edit" 按钮。EditCommandColumn控件在DataGrid里新添一列,为每一行记录放置一个编辑按钮,当点击该按钮时,将引起页面回传并触发EditCommand事件。需要为这个事件写代码。

<asp:DataGrid id="dgProducts" runat="server"

   AutoGenerateColumns="False" CellPadding="4"

   HeaderStyle-BackColor="Black"

   HeaderStyle-ForeColor="White"

   HeaderStyle-HorizontalAlign="Center"

   HeaderStyle-Font-Bold="True">

       

   <Columns>

    <asp:EditCommandColumn EditText="Edit Info"

          ButtonType="PushButton"

          UpdateText="Update" CancelText="Cancel" />

       <asp:BoundColumn HeaderText="Product ID" DataField="ProductID"

               ReadOnly="True" />               

       <asp:BoundColumn HeaderText="Price" DataField="UnitPrice"

               ItemStyle-HorizontalAlign="Right"

               DataFormatString="{0:$#,###.##}" />

       <asp:BoundColumn HeaderText="Name" DataField="ProductName" />

       <asp:BoundColumn HeaderText="Description"

               DataField="ProductDescription" />                       

   </Columns>       

</asp:DataGrid>

    EditCommandColumn控件有很多可选属性,比如:ButtonType,它指定按钮类型为超链接(默认为LinkButton)或PushButton;以及为Edit,Update和Cancel按钮指定text的 EditText, UpdateText和CancelText属性;此外,还有其它各种的显示属性,比如HeaderText, ItemStyle等等。

    编辑状态时,除了要编辑的那一行外,每一行都包含一个编辑按钮,而要编辑的那一行却显示的是2个按钮,Update"和"Cancel"按钮。

选择一行供编辑

    DataGrid有一个名为EditItemIndex的属性,它指定那一行记录要编辑。DataGrid从0开始为它包含的行记录排顺序。默认情况下,当没有哪行记录要编辑时EditItemIndex的值为-1.当点击某行记录的"Edit"时,将触发EditCommand事件处理器。在该事件处理器里,我们将EditItemIndex属性与要编辑的那一行对应起来,然后重新绑定DataGrid的数据(调用BindData()),代码看起来像下面的这样:

Sub dgProducts_Edit(sender As Object, e As DataGridCommandEventArgs)

    dgProducts.EditItemIndex = e.Item.ItemIndex

    BindData()

End Sub

    将该事件处理器与EditCommand事件对应起来,在DataGrid里进行指定,如下:

<asp:DataGrid id="dgProducts" runat="server"

   ...  

   OnEditCommand="dgProducts_Edit"

   ... >

       

   <Columns>

       ...

   </Columns>       

</asp:DataGrid>

    有了这些代码,当用户点击"Edit"按钮时,页面发生回传,待编辑行的"Edit" 按钮替换成了"Update"和"Cancel"按钮。另外,方格里的数值由文本值(textual value)自动的转换成处于可编辑的textbox类型的数值了。要想使DataGrid里的某些列不可编辑,只需要将其ReadOnly属性设置为True。

<asp:BoundColumn HeaderText="Product ID" DataField="ProductID" ReadOnly="True" />

可以为处于编辑状态的行设置各种各样的格式属性,方法是使用DataGrid的EditItemStyle属性。比如:

<asp:DataGrid id="dgProducts" runat="server"

   ...  

   EditItemStyle-BackColor="#eeeeee"

   ... >

       

   <Columns>

       ...

   </Columns>       

</asp:DataGrid

探讨用户更新或取消更新所必需的代码

取消“Cancel” 按钮

    当点击某行的"Edit"按钮时,它会被替换为"Update"和"Cancel" 按钮."Cancel" 意味着DataGrid恢复到“非编辑”状态,并不会保存所做的任何修改。当点击"Cancel"按钮时触发CancelCommand事件.在对应的事件处理器里我们需要设置DataGrid返回到“预编辑”(pre-editing)状态。为此,只需要将EditItemIndex属性设为“-1”,并重新对DataGrid进行绑定即可(调用BindData()):

Sub dgProducts_Cancel(sender As Object, e As DataGridCommandEventArgs)

    dgProducts.EditItemIndex = -1 //dgProducts为一个DataGrid,DataGrid有此属性

    BindData()

End Sub

更新“Update” 按钮

    当点击“Update” 按钮时触发UpdateCommand事件。在对应的事件处理器里,需要读取用户键入textboxe的值,然后构造一个恰当的SQL语句。“Update” 按钮的事件处理器接受2个输入参数,一个为Object,另一个为DataGridCommandEventArgs.并且后者有一个Item属性,它是DataGridItem的一个实例,与要编辑的那个DataGrid row相对应。DataGridItem包含一个单元集(Cells collection),可以通过它来访问DataGrid里各种列包含的text或控件.通过DataGridItem对象,可以探测所编辑行的ProductID,甚至用户输入textboxe里的price,name,description值:

Sub dgProducts_Update(sender As Object, e As DataGridCommandEventArgs)

   'Read in the values of the updated row

   Dim iProductID as Integer = e.Item.Cells(1).Text

//CTye函数将e.Item.Cells(2).Controls(0)转换成TextBox类型并返回,所以后面可以跟.text

   Dim dblPrice as Double = CType(e.Item.Cells(2).Controls(0),TextBox).Text

   Dim strName as String = CType(e.Item.Cells(3).Controls(0),TextBox).Text

   Dim strDesc as String = CType(e.Item.Cells(4).Controls(0),TextBox).Text

    ...

    在编辑模式里,ProductID列并不是textbox类型的(因为它被标明为只读ReadOnly)。注意到在代码里ProductID的值可以通过e.Item.Cells(1)的Text属性来获取。Cells(1)代表的是DataGrid row里的第二个表格单元(TableCell)。而第一个表格单元(也就是Cells(0))就是包含 "Update"和 "Cancel"按钮的那个列。要获取price, name,description的值就要稍微复杂一些,因为这些值是包含在表格单元里的textbox的文本值。因此,要用CType函数将相应表格单元的第一个控件(Controls(0))(为什么Controls(0)指第一个控件,第一个控件为什么又会是TextBox而不是其他呢?)转换(cast)为一个TextBox控件,然后引用它的Text属性。(如果用的是C#,要这样转换:((TextBox) e.Items.Cells[2].Controls[0]).Text,而CType()函数是VB.NET的内置函数)

    获取这些值以后我们创建一个SQL UPDATE命令。在前面的文章里我们用的是动态SQL语句(dynamic SQL statement),不过在这里我用的是带参数的形式,因为我觉得这样要更简洁一些——当然,你喜欢用哪种都可以。

...

       

    'Construct the SQL statement using Parameters

    Dim strSQL as String = _

      "UPDATE [Products] SET [ProductName] = @ProdName, " & _

      "[UnitPrice] = @UnitPrice, [ProductDescription] = @ProdDesc " &_

      "WHERE [ProductID] = @ProductID"

    Const strConnString as String ="Provider=Microsoft.Jet.OLEDB.4.0;Data

Source=C:\GrocerToGo.mdb"

    Dim objConn as New OleDbConnection(strConnString)

    objConn.Open()

    Dim myCommand as OleDbCommand = new OleDbCommand(strSQL, objConn)

    myCommand.CommandType = CommandType.Text

    ' Add Parameters to the SQL query

    Dim parameterProdName as OleDbParameter = _

               new OleDbParameter("@ProdName", OleDbType.VarWChar, 75)

    parameterProdName.Value = strName

    myCommand.Parameters.Add(parameterProdName)

    Dim parameterUnitPrice as OleDbParameter = _

               new OleDbParameter("@UnitPrice", OleDbType.Currency)

    parameterUnitPrice.Value = dblPrice

    myCommand.Parameters.Add(parameterUnitPrice)

    Dim parameterProdDesc as OleDbParameter = _

               new OleDbParameter("@ProdDesc", OleDbType.VarWChar)

    parameterProdDesc.Value = strDesc

    myCommand.Parameters.Add(parameterProdDesc)

    Dim parameterProdID as OleDbParameter = _

               new OleDbParameter("@ProductID", OleDbType.Integer)

    parameterProdID.Value = iProductID

    myCommand.Parameters.Add(parameterProdID)

    myCommand.ExecuteNonQuery()   'Execute the UPDATE query

   

    objConn.Close()   'Close the connection

...

    现在,数据库已经对用户的编辑进行了更新,剩下要做的是使DataGrid返回预编辑状态(通过设置EditItemIndex为-1),然后对DataGrid进行绑定(通过BindData())。

...

  

   'Finally, set the EditItemIndex to -1 and rebind the DataGrid

    dgProducts.EditItemIndex = -1

    BindData()       

End Sub

    最后,完善代码,你需要将CancelCommand和UpdateCommand事件与相应的事件处理器对应起来,像下面这样:

<asp:DataGrid id="dgProducts" runat="server"

   ...  

   OnUpdateCommand="dgProducts_Update"

   OnCancelCommand="dgProducts_Cancel"

   ... >

       

   <Columns>

       ...

   </Columns>       

</asp:DataGrid>

    在编辑界面里,默认的是TextBoxes控件,可以对界面进行定制使其更具灵活性。定制DataGrid的编辑界面需要额外的代码,不过这些都比较简单,难的是理解发生在页面背后的运行机制原理。

用EditItemTemplate定制编辑界面

    可以向DataGrid添加模版列以自定义HTML输出效果。如果是绑定列(BoundColumn)的话,默认的是TextBox界面。

    可以在TemplateColumn控件里使用EditItemTemplate。在编辑模式里用DropDownList来显示category。为此只需要添加DropDownList控件即可,像这样:

<asp:TemplateColumn HeaderText="Category">

      <ItemTemplate>

        <%# DataBinder.Eval(Container.DataItem, "CategoryName") %>

      </ItemTemplate>

     

      <EditItemTemplate>

        <asp:DropDownList runat="server" id="lstCategories"

                DataValueField="FAQCategoryID"

                DataTextField="Name"

                DataSource="???" />

       </EditItemTemplate>               

</asp:TemplateColumn>

    当对DropDownList进行绑定时需要指定DropDownList显示的文字和对应的传递值。比较妥当的做法是DropDownList将种类的名称(即Name列)显示出来,而传递的值是AQCategoryID。当指定DropDownList绑定哪些列时,实际上你已经指定了数据源。从本质上来说,我们需要用表tblFAQCategory的行记录来构建一个数据集(DataSet)。//本例DataSet包含了2个字段去填充DropDownList,用来显示文字和作为传递值

在深入研究以前,先考察一下当编辑DataGrid里的数据时,在后台到底发生了什么呢?

DataGrid的幕后机制

    调用DataGrid的DataBind()方法时,它枚举数据源的内容。对数据源里的每一项(item),相应的添加一个DataGridItem实例。每个DataGridItem都有一个ItemType属性,要么被标明为Item、或者AlternatingItem,又或者EditItem及其它ItemType类型。添加的第一个(以及所有为奇数)的DataGridItem被标明为一个ItemType of Item(也就是Item、AlternatingItem或EditItem);而添加的第二个(以及所有为偶数)的DataGridItem被标明为AlternateItem。

    一旦指定DataGridItem的ItemType属性后,便可以运用相应的用户界面属性。打个比方,如果指定ItemType属性为Item,那么就可以使用Item对应的ItemStyle属性了。我们知道,DataGrid用Table类(Table class)来控制显示界面(明显的,DataGrid呈现为一个HTML表格);此外DataGridItem源自于TableRow class类,那就意味着DataGridItem对象将呈现为一个HTML表格行(TABLE row).

    DataGrid class类有一个EditItemIndex属性,对DataGrid进行编辑时就要用到这个属性。比如,在DataGrid的OnEditCommand事件处理器里,要做的是将DataGrid的EditItemIndex设置为点击了"Edit"按钮的那一行的index值。 当枚举数据源时,如果当前行与EditItemIndex相匹配,那么就将该DataGridItem行标记为EditItem,进行编辑。编辑时,如果某列为绑定列(BoundColumn),该列就呈现为默认的TextBox,该TextBox的Text属性的值就是用户编辑输入的值;如果某列为模版列(TempalteColumn),那么就呈现为它的EditItemTemplate(如果设置为available的话)。

    综上,当调用DataGrid的DataBind()方法时,将对数据源进行枚举,数据将一行一行的添加到DataGrid(DataGrid从本质来说就是一个HTML表格)。有一点很重要,对添加到DataGrid的每一行("row")来说,可以对其使用任何的数据绑定语法,就本章而言,我们就在模版列的ItemTemplate里使用了数据绑定语法(the <%# ... %>)。此外,行里的任何控件也有自己的DataBind()方法。

    言归正传,对放置在EditItemTemplate标签里的DropDownList控件来说,当调用DataGrid的DataBind()方法时,“处于编辑状态的”DropDownList的DataBind()也会被调用。基于前面的知识,可以推断出应该将DropDownList的数据源设置为某个数据集(DataSet)并自动调用其DataBind()方法。

指定DropDownList的DataSource属性

    使用数据绑定语句来指定DropDownList的DataSource属性,具体来说,用表tblFAQCategory来填充一个数据集,再用一个函数将该数据集返回,像下面这样:

<asp:TemplateColumn HeaderText="Category">

      <ItemTemplate>

        <%# DataBinder.Eval(Container.DataItem, "CategoryName") %>

      </ItemTemplate>

     

      <EditItemTemplate>

        <asp:DropDownList runat="server" id="lstCategories"

                DataValueField="FAQCategoryID"

                DataTextField="Name"

                DataSource="<%# GetCategories() %>" />

       </EditItemTemplate>              

</asp:TemplateColumn>

    函数GetCategories()仅仅返回一个由表tblFAQCategory的行进行填充的数据集。这个函数很简单,像下面这样:

<% @Import Namespace="System.Data" %>

<% @Import Namespace="System.Data.SqlClient" %>

<script language="vb" runat="server">

'Create a connection

Dim myConnection as New SqlConnection(connString)

Dim ddlDataSet as DataSet = New DataSet()

Function GetCategories() as DataSet

//包含2个字段FAQCategoryID和Name,填充一个DropDownList(DataValueField="FAQCategoryID" DataTextField="Name")

         'Populate the ddlDataSet

    Const strSQLDDL as String ="SELECT FAQCategoryID, Name FROM tblFAQCategory ORDER BY Name"   

    Dim myDataAdapter as SqlDataAdapter = New SqlDataAdapter(strSQLDDL, myConnection)   

    myDataAdapter.Fill(ddlDataSet, "Categories")

    Return ddlDataSet

End Function

...

演示页面为:http://aspnet.4guysfromrolla.com/demos/dgExample15.aspx

    注意,数据库连接(connection)和数据集ddlDataSet都是在页面上定义的,这意味着ASP.NET Web 页面上的任何一个函数都可以访问这2个对象。对数据库连接对象进行完整(globally)定义的原因是我们要用到2个获取数据库信心的函数—GetCategories() 和 BindData()。相比起来,与其为这2个函数分别创建creating, opening和closing的分段连接对象,还不如直接使用完整的数据库连接对象。请注意,该连接对象在达到目的时(也就是当页面完全显示出来后)自动关闭。

    GetCategories()函数简单易懂,只是用表tblFAQCategory的查询结果来填充数据集ddlDataSet,再将填充好了的数据集返回。在演示页面里当点击"Edit"按钮时,DropDownList控件确实被绑定了,再仔细看看,那发现了什么问题?对了,当点击某行的"Edit"按钮时,DropDownList显示的是数据集里第一行的类类名,而非其本来的类名。//点编辑时DropDownList显示项不是原来对应的项,需要改进

确保DropDownList恰当的Selected Item

    在编辑模式里,为确保DropDownList有恰当的Selected Item,需要编程设置DropDownList的SelectedIndex属性。为此,需要使用数据绑定语法,具体的说,要调用一个函数,将所选择的FAQ的FAQCategoryID值传给它,代码如下:

<asp:TemplateColumn HeaderText="Category">

<ItemTemplate>

    <%# DataBinder.Eval(Container.DataItem, "CategoryName") %>

</ItemTemplate>

     

<EditItemTemplate>

    <asp:DropDownList runat="server" id="lstCategories"

         DataValueField="FAQCategoryID"

         DataTextField="Name"

         DataSource="<%# GetCategories() %>"

         SelectedIndex='<%# GetSelIndex(Container.DataItem("FAQCategoryID")) %>'

    />

   </EditItemTemplate>              

</asp:TemplateColumn>

    函数GetSelIndex()接收的是编辑行的FAQCategoryID值(那就是为什么要在DataGrid里包含FAQCategoryID列,虽然并不在DataGrid里将该列显示出来)。DropDownLis控件的SelectedIndex属性是一个integer类型的值,且大小介于0和它包含的项的总数(number of items)之间。所以,函数GetSelIndex()返回一个integer类型的值,它对应DropDownList的所选项。数据集ddlDataSet用来填充(populate)控件DropDownList,同时数据集ddlDataSet每行的index与DropDownList的每项的index一一对应。所以,函数GetSelIndex()遍历数据集,将每行与传入的FAQCategoryID值相比较。一旦匹配的话,函数就返回该行的index,然后对DropDownList的SelectedIndex属性赋值

Function GetSelIndex(CatID as String) as Integer

Dim iLoop as Integer

'Loop through each row in the DataSet

Dim dt as DataTable = ddlDataSet.Tables("Categories")

For iLoop = 0 to dt.Rows.Count - 1

    If Int32.Parse(CatID) = _

          Int32.Parse(dt.Rows(iLoop)("FAQCategoryID")) then

      Return iLoop

    End If

Next iLoop

End Function

演示页面为:http://aspnet.4guysfromrolla.com/demos/dgExample16.aspx

在OnUpdateCommand事件处理器里检索DropDownList的值

    当用户编辑某行记录时,将会显示"Update" 和 "Cancel"按钮供选择。当用户点击"Update"按钮时将触发OnUpdateCommand事件。为了用输入的数据对数据库进行更新,我们需要为OnUpdateCommand事件编写一个事件处理器。

    为了获取用户从DropDownList选取的值,使用FindControl方法来得到DropDownList控件的实例;访问SelectedItem.Value属性,也就是用户在DropDownList控件里所选的条目的FAQCategoryID值。OnUpdateCommand事件处理器的代码如下:

Sub dgPopFAQs_Update(sender As Object, e As DataGridCommandEventArgs)//都是VB语法,CType是VB内置函数,C#要用强制转换

    'Determine what category was selected

    Dim strCategoryID as String, strCategoryName as String   

    strCategoryID = CType(e.Item.FindControl("lstCategories"), _

                          DropDownList).SelectedItem.Value

    strCategoryName = CType(e.Item.FindControl("lstCategories"), _

                            DropDownList).SelectedItem.Text

   

    ... Make a SQL call to update the database ...

    'Return the DataGrid to its pre-editing state

    dgPopularFAQs.EditItemIndex = -1

    BindData()

End Sub

演示页面为:http://aspnet.4guysfromrolla.com/demos/dgExample17.aspx

使用EditItemTemplate定制其它编辑界面

    在本文考察了使用EditItemTemplate来包含一个DropDownList控件。当然,也可以用它来为DataGrid里的其它列进行编辑界面定制。

  评论这张
 
阅读(1815)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018