考察DataGrid控件 Part 7

系统 1849 0

考察DataGrid控件 Part 7 (上)

本文英文原版:
http://aspnet.4guysfromrolla.com/articles/080702-1.aspx


导言:

在编辑界面里,默认的是TextBoxes控件,不过你可以对界面进行定制使其更具灵活性。比如,假如DataGrid控件里的某个列是True/False域,我们展现一对True/False单选项要比默认的TextBoxes控件要好的多;再比如,假设一个列为某个表的外键(foreign key),我们提供一个DropDownList控件供用户选择恰当的选项或许是个更好的办法。定制DataGrid的编辑界面需要额外的代码,不过这些都比较简单,我个人认为,难的是理解发生在页面背后的运行机制原理。


用EditItemTemplate定制编辑界面

为了定制DataGrid的编辑界面,我们要用到模版列(TemplateColumn)。记得在本系列文章的Part 5部分提到过,可以向DataGrid添加模版列以自定义HTML输出效果。如果是绑定列(BoundColumn)的话,默认的是TextBox界面。在本文,我们要用到的是一个real-world示例。在本系列的前面部分,我们看到的示例关于ASPFAQs.com上的常见问题解答。用到的是FAQ数据库,它有一系列的数据表,其中最主要的是tblFAQ表,每一行记录对应一条FAQ(即常见问题解答)。其中一个列FAQCategoryID,它是数据表tblFAQCategory的一个外键,该数据表的每一行记录对应一个FAQ种类(这些种类包括Array, Appilcation Object, Email, ASP.NET, Forms等等) 。下面是这2个表的重要部分的定义:

考察DataGrid控件 Part 7
基于前6章的基础知识,你应该快速而容易的创建一个DataGrid来显示每条FAQ,包括FAQ的种类名称(注意不是integer类型的)。下面的SQL语句可以挑选出每条FAQ的种类名称:
SELECT FAQID, F.FAQCategoryID, Name AS CategoryName, Description
FROM tblFAQ F
INNER JOIN tblFAQCategory FC ON FC.FAQCategoryID = F.FAQCategoryID

(演示页面为 http://aspnet.4guysfromrolla.com/demos/dgExample13.aspx )

现在假设你想让用户编辑DataGrid,你可能会想,“哈哈,我已经看完前6章,我知道怎么做了,首先添加一个EditCommandColumn!”不错,这是正确的第一步,不过我们还要做更多的工作,因为默认时,绑定列呈现的编辑界面不太理想。看一下这个演示页面

http://aspnet.4guysfromrolla.com/demos/dgExample14.aspx ),点击某行的Edit按钮,你会看到什么?要是你懒得点这个演示页面的话,下面的这个图显示了最终效果。

考察DataGrid控件 Part 7

我们注意到在编辑模式里,列Category已经转变成了默认的TextBox。咋一看好像没什么问题,假设某人想把编号为2的Category由Strings变为Arrays,他只需要键入Arrays。然而,从各个方面考虑这并不是最优方案。首先,Category列是数据表tblFAQCategory的外键,虽然你可以遍历tblFAQCategory来查找恰当的category名称,然后用对应的FAQCategoryID值来更新处于编辑状态的tblFAQ row,但这样显的有点凌乱。再者,假如用户输入有误呢,比如将“Strings”输入成“String”?你怎么办呢?弹出一个错误消息吗?假设用户是有意的,那么要在tblFAQCategory里新添加一行记录吗?

明显,理想情况是在Category列将TextBox替换成listbox,设置一些选项供用户选择。为此,我们需要在模版列里指定一个EditItemIndex控件,当用户选择一行编辑时,达到自定义HTML输出效果的目的。前面提到过要自定义编辑界面,我们要用到模版列(TemplateColumn),而非绑定列(BoundColumn)。因此,首先我们需要将显示category的绑定列转换为模版列,如下面的代码所显(关于模版列的更多信息请参考Part 5):

<asp:datagrid id="dgPopularFAQs" runat="server" ... >
<Columns>
<asp:EditCommandColumn EditText="Edit" CancelText="Cancel"
UpdateText="OK" />

<asp:BoundColumn DataField="FAQID" ItemStyle-Width="10%"

ReadOnly="True"
ItemStyle-HorizontalAlign="Center"

HeaderText="FAQ ID" />

<asp:TemplateColumn HeaderText="Category">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "CategoryName") %>
</ItemTemplate>
</asp:TemplateColumn>

<asp:BoundColumn DataField="Description" HeaderText="FAQ Question"

/>
</Columns>
</asp:datagrid>

在此,我们仅仅从数据源将CategoryName列展示出来,还没完;当前,如果用户点击“Edit”按钮的话,并不会呈现出某个category列,换句话说,他们只会看到category的text文本,而不是TextBox,DropDownList等。这是因为在编辑模式里编辑某行记录的话,我们不使用绑定列那么就必须使用HTML。为此我们需要在TemplateColumn控件里使用EditItemTemplate(好比只显示而不编辑数据时在ItemTemplate里用HTML呈现效果)

因此,在编辑模式里我们用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显示现有的FAQ种类,我们需要将DropDownList绑定到一个数据源。当对DropDownList进行绑定时我们需要指定DropDownList显示的文字和对应的传递值。既然DropDownList显示的每一项对应表tblFAQCategory里的一行记录,比较妥当的做法是DropDownList将种类的名称(即Name列)显示出来,而传递的值是AQCategoryID,因为在表tblFAQCategory里一个AQCategoryID对应一个Name,而AQCategoryID是主键值(如果你对此完全被搞糊涂了,我建议你参考这篇文章:Creating Databound DropDown Lists in ASP.NET http://www.4guysfromrolla.com/webtech/071101-1.shtml )。这就是我在上文提到DataValueField和DataTextFields的原因。

当然,我们应该明白这样道理:当你指定DropDownList绑定哪些列时,实际上你已经指定了数据源。从本质上来说,我们需要用表tblFAQCategory的行记录来构建一个数据集(DataSet),在深入研究以前,我们先考察一下当编辑DataGrid里的数据时,在后台到底发生了什么呢?


考察DataGrid控件 Part 7 (中)

本文英文原版:
http://aspnet.4guysfromrolla.com/articles/080702-1.2.aspx


DataGrid的幕后机制

调用DataGrid的DataBind()方法时,它枚举数据源的内容。对数据源里的每一项(item),相应的添加一个DataGridItem实例。每个DataGridItem都有一个ItemType属性,要么被标明为Item、或者AlternatingItem,又或者EditItem(当然还有其它ItemType类型,不过对本例而言,我们只关注这3种)。添加的第一个(以及所有为奇数)的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属性

我们使用我们熟悉的数据绑定语句来指定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
'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显示的是数据集里第一行的类类名,而非其本来的类名。比如,第一个FAQ的类名是"Strings",当你点击第一行的"Edit"按钮后,显示的却是"Application Object"。还好,要修补这个小问题我们只需要使用一些数据绑定语法和代码,我们在最后一部分再讨论。

考察DataGrid控件 Part 7 (下)
本文英文原版
http://aspnet.4guysfromrolla.com/articles/080702-1.3.aspx

确保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)之间。比方,我们想将选择第五项,我们只需要将SelectedIndex属性设置为4(因为第一项的index值为0)

所以,函数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"替换为"OK"了)。当用户点击"Update"按钮时将触发OnUpdateCommand事件。就像在Part 6探讨过的一样,为了用输入的数据对数据库进行更新,我们需要为OnUpdateCommand事件编写一个事件处理器。

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

Sub dgPopFAQs_Update(sender As Object, e As DataGridCommandEventArgs)
'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里的其它列进行编辑界面定制。比如,你可能觉得DataGrid默认生成的TextBox 太小,你可以用EditItemTemplate来增大TextBox 的尺寸。另一个比较有实际意义的运用是,假如有一个Yes/No类型的列,你可以添加一个包含"Yes" / "No"值的DropDownList控件或干脆设置一个 单选按钮,这比使用TextBox要好的多。

注意演示页面,在编辑界面里我们使用的是75-column的TextBox.

结语:

本文对Part 6的内容进行了扩展,允许对DataGrid的编辑界面进行用户定制。我们首先探讨了如何来模版列里用EditItemTemplate对某个列进行编辑界面定制。然后,考察了如何将表tblFAQCategory的内容绑定到DropDownList,以及自动的选择恰当的条目(item)。最后,我们学习了如何读取用户从DropDownList选取的值;这一步很重要,它关系到用用户期望的值对数据库进行更新。

谨记,用EditItemTemplate对编辑界面进行用户定制的潜力是无穷的,只是看你的能力而已!

祝编程快乐!

考察DataGrid控件 Part 7


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论