作者 :斯科特·米切尔
了解如何创建完全可编辑的 DataList,其中所有项都处于编辑模式,并且可以通过单击页面上的“全部更新”按钮保存其值。
简介
在前面的教程中,我们研究了如何创建项目级 DataList。 与标准可编辑 GridView 一样,DataList 中的每个项都包含一个“编辑”按钮,单击该按钮会使项目可编辑。 虽然此项级编辑适用于仅偶尔更新的数据,但某些用例方案要求用户编辑许多记录。 如果用户需要编辑数十条记录,并被迫单击“编辑”,进行更改,然后单击每个记录的“更新”,则单击次数可能会阻碍她的工作效率。 在这种情况下,更好的选择是提供完全可编辑的 DataList,其中 所有 项都处于编辑模式,并且可以通过单击页面上的“全部更新”按钮来编辑其值(请参阅图 1)。
图 1:可以修改完全可编辑 DataList 中的每个项(单击以查看全尺寸图像)
本教程介绍如何让用户使用完全可编辑的 DataList 更新供应商地址信息。
步骤 1:在 DataList s ItemTemplate 中创建可编辑用户界面
在前面的教程中,我们在创建标准项级可编辑 DataList 时使用了两个模板:
-
ItemTemplate包含只读用户界面(用于显示每个产品名称和价格的标签 Web 控件)。 -
EditItemTemplate包含编辑模式用户界面(两个 TextBox Web 控件)。
DataList s 属性指示使用 EditItemIndexEditItemTemplate(如果有)。 具体而言, DataListItem 其 ItemIndex 值与 DataList 属性 EditItemIndex 匹配的呈现方式 EditItemTemplate是使用 . 当一次只能编辑一个项,但在创建完全可编辑的 DataList 时,此模型可正常工作。
对于完全可编辑的 DataList,我们希望 所有DataListItem S 都使用可编辑接口呈现。 实现此目的的最简单方法是在 中 ItemTemplate定义可编辑接口。 对于修改供应商地址信息,可编辑接口包含供应商名称作为文本,然后包含地址、城市和国家/地区值的 TextBoxes。
首先打开 BatchUpdate.aspx 页面,添加 DataList 控件,并将其属性设置为 IDSuppliers。 从 DataList 的智能标记中,选择添加名为 SuppliersDataSource 的新 ObjectDataSource 控件。
图 2:创建名为 SuppliersDataSource 的新 ObjectDataSource (单击可查看全尺寸图像)
将 ObjectDataSource 配置为使用 SuppliersBLL 类 s GetSuppliers() 方法检索数据(请参阅图 3)。 与前面的教程一样,我们将直接使用业务逻辑层,而不是通过 ObjectDataSource 更新供应商信息。 因此,将“更新”选项卡中的下拉列表设置为“无”(请参阅图 4)。
图 3:使用 GetSuppliers() 方法检索供应商信息(单击以查看全尺寸图像)
图 4:将“更新”选项卡中的下拉列表设置为“无”(单击以查看全尺寸图像)
完成向导后,Visual Studio 自动生成 DataList, ItemTemplate 以显示标签 Web 控件中数据源返回的每个数据字段。 我们需要修改此模板,以便它改为提供编辑界面。
ItemTemplate可以使用 DataList 智能标记中的“编辑模板”选项或通过声明性语法直接通过设计器自定义。
花点时间创建一个编辑界面,该界面将供应商的名称显示为文本,但包括供应商地址、城市和国家/地区值的 TextBoxes。 进行这些更改后,页面的声明性语法应如下所示:
<asp:DataList ID="Suppliers" runat="server" DataKeyField="SupplierID"
DataSourceID="SuppliersDataSource">
<ItemTemplate>
<h4><asp:Label ID="CompanyNameLabel" runat="server"
Text='<%# Eval("CompanyName") %>' /></h4>
<table border="0">
<tr>
<td class="SupplierPropertyLabel">Address:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Address" runat="server"
Text='<%# Eval("Address") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">City:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="City" runat="server"
Text='<%# Eval("City") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">Country:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Country" runat="server"
Text='<%# Eval("Country") %>' />
</td>
</tr>
</table>
<br />
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
</asp:ObjectDataSource>
注意
与前面的教程一样,本教程中的 DataList 必须启用其视图状态。
在ItemTemplate我使用的是两个新的 CSS 类,SupplierPropertyLabel并且SupplierPropertyValue已添加到Styles.css该类,并配置为使用与 CSS 类相同的样式设置ProductPropertyLabelProductPropertyValue。
.ProductPropertyLabel, .SupplierPropertyLabel
{
font-weight: bold;
text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
padding-right: 35px;
}
进行这些更改后,通过浏览器访问此页面。 如图 5 所示,每个 DataList 项将供应商名称显示为文本,并使用 TextBoxes 显示地址、城市和国家/地区。
图 5:DataList 中的每个供应商都是可编辑的(单击以查看全尺寸图像)
步骤 2:添加“全部更新”按钮
虽然图 5 中的每个供应商都有其地址、城市和国家/地区字段显示在 TextBox 中,但目前没有可用的“更新”按钮。 通常页面上有一个“全部更新”按钮,而不是每个项目都有一个“更新”按钮,其中通常有一个“更新”按钮,单击后,更新 DataList 中的所有 记录。 在本教程中,让我们添加两个“全部更新”按钮-一个位于页面顶部,一个位于底部(尽管单击任一按钮将具有相同的效果)。
首先,在 DataList 上方添加一个 Button Web 控件,并将其 ID 属性设置为 UpdateAll1。 接下来,在 DataList 下面添加第二个按钮 Web 控件,将其 ID 设置为 UpdateAll2。 将 Text 两个按钮的属性设置为“全部更新”。 最后,为这两个 Buttons Click 事件创建事件处理程序。 让我们将该逻辑重构到第三个方法,而不是复制每个事件处理程序中的更新逻辑, UpdateAllSupplierAddresses让事件处理程序只调用第三种方法。
Protected Sub UpdateAll1_Click(sender As Object, e As EventArgs) _
Handles UpdateAll1.Click
UpdateAllSupplierAddresses()
End Sub
Protected Sub UpdateAll2_Click(sender As Object, e As EventArgs) _
Handles UpdateAll2.Click
UpdateAllSupplierAddresses()
End Sub
Private Sub UpdateAllSupplierAddresses()
' TODO: Write code to update _all_ of the supplier addresses in the DataList
End Sub
图 6 显示添加“全部更新”按钮后的页面。
图 6:已将两个更新所有按钮添加到页面(单击以查看全尺寸图像)
步骤 3:更新所有供应商地址信息
由于所有 DataList 项都显示编辑界面,并添加了“全部更新”按钮,剩下的所有项都是编写代码来执行批处理更新。 具体而言,我们需要循环访问 DataList 项,并为每个项调用 SuppliersBLL 类 s UpdateSupplierAddress 方法。
可以通过 DataList 属性访问构成 DataList 的DataListItem实例集合。Items 通过对 a DataListItem的引用,我们可以从SupplierID集合中获取相应的内容DataKeys,并按编程方式引用 TextBox Web 控件,ItemTemplate如以下代码所示:
Private Sub UpdateAllSupplierAddresses()
' Create an instance of the SuppliersBLL class
Dim suppliersAPI As New SuppliersBLL()
' Iterate through the DataList's items
For Each item As DataListItem In Suppliers.Items
' Get the supplierID from the DataKeys collection
Dim supplierID As Integer = Convert.ToInt32(Suppliers.DataKeys(item.ItemIndex))
' Read in the user-entered values
Dim address As TextBox = CType(item.FindControl("Address"), TextBox)
Dim city As TextBox = CType(item.FindControl("City"), TextBox)
Dim country As TextBox = CType(item.FindControl("Country"), TextBox)
Dim addressValue As String = Nothing, _
cityValue As String = Nothing, _
countryValue As String = Nothing
If address.Text.Trim().Length > 0 Then
addressValue = address.Text.Trim()
End If
If city.Text.Trim().Length > 0 Then
cityValue = city.Text.Trim()
End If
If country.Text.Trim().Length > 0 Then
countryValue = country.Text.Trim()
End If
' Call the SuppliersBLL class's UpdateSupplierAddress method
suppliersAPI.UpdateSupplierAddress _
(supplierID, addressValue, cityValue, countryValue)
Next
End Sub
当用户单击“全部更新”按钮之一时,该方法 UpdateAllSupplierAddresses 循环访问 DataListItem DataList 中的每个 Suppliers 按钮并调用 SuppliersBLL 类的方法 UpdateSupplierAddress ,并传入相应的值。 地址、城市或国家/地区传递的非输入值是一个值 Nothing ( UpdateSupplierAddress 而不是空白字符串),这会导致基础记录字段的数据库 NULL 。
注意
作为增强功能,你可能希望向页面添加状态标签 Web 控件,该页面在执行批处理更新后提供一些确认消息。
仅更新已修改的地址
本教程中使用的批处理更新算法调用 UpdateSupplierAddressDataList 中每个 供应商的方法,无论其地址信息是否已更改。 虽然此类盲目更新通常不是性能问题,但如果正在审核数据库表的更改,它们可能会导致多余的记录。 例如,如果使用触发器将所有 UPDATE 记录到 Suppliers 审核表中,则每当用户单击“全部更新”按钮时,系统中的每个供应商都会创建新的审核记录,而不管用户是否进行任何更改。
ADO.NET DataTable 和 DataAdapter 类旨在支持仅修改、删除和新记录会导致任何数据库通信的批处理更新。 DataTable 中的每个行都有一个 RowState 属性,该属性 指示该行是否已添加到 DataTable、从其中删除、修改或保持不变。 最初填充 DataTable 时,所有行都标记为未更改。 更改任何行列的值会将行标记为已修改。
在类中SuppliersBLL,我们先在单个供应商记录SuppliersDataTable中读取指定的供应商地址信息,然后使用以下代码设置Address和CityCountry列值:
Public Function UpdateSupplierAddress _
(supplierID As Integer, address As String, city As String, country As String) _
As Boolean
Dim suppliers As Northwind.SuppliersDataTable = _
Adapter.GetSupplierBySupplierID(supplierID)
If suppliers.Count = 0 Then
' no matching record found, return false
Return False
Else
Dim supplier As Northwind.SuppliersRow = suppliers(0)
If address Is Nothing Then
supplier.SetAddressNull()
Else
supplier.Address = address
End If
If city Is Nothing Then
supplier.SetCityNull()
Else
supplier.City = city
End If
If country Is Nothing Then
supplier.SetCountryNull()
Else
supplier.Country = country
End If
' Update the supplier Address-related information
Dim rowsAffected As Integer = Adapter.Update(supplier)
' Return true if precisely one row was updated, otherwise false
Return rowsAffected = 1
End If
End Function
无论值是否已更改,此代码都会将传入的地址、城市和国家/地区值分配给传入SuppliersRow的地址、城市和国家/地区值SuppliersDataTable。 这些修改会导致 SuppliersRow 将 s RowState 属性标记为已修改。 调用数据访问层的方法 Update 时,会看到 SupplierRow 该方法已修改,因此会向数据库发送命令 UPDATE 。
但是,假设我们向此方法添加了代码,以便在传入地址、城市和国家/地区值与 SuppliersRow 现有值不同时仅分配传入地址、城市和国家/地区值。 如果地址、城市和国家/地区与现有数据相同,则不会进行 SupplierRow 任何更改,并且 s RowState 将标记为未更改。 净结果是,调用 DAL 方法 Update 时,不会进行任何数据库调用,因为 SuppliersRow 尚未修改数据库。
若要执行此更改,请将盲目分配传入地址、城市和国家/地区值的语句替换为以下代码:
' Only assign the values to the SupplierRow's column values if they differ
If address Is Nothing AndAlso Not supplier.IsAddressNull() Then
supplier.SetAddressNull()
ElseIf (address IsNot Nothing AndAlso supplier.IsAddressNull) _
OrElse (Not supplier.IsAddressNull() AndAlso _
String.Compare(supplier.Address, address) <> 0) Then
supplier.Address = address
End If
If city Is Nothing AndAlso Not supplier.IsCityNull() Then
supplier.SetCityNull()
ElseIf (city IsNot Nothing AndAlso supplier.IsCityNull) _
OrElse (Not supplier.IsCityNull() AndAlso _
String.Compare(supplier.City, city) <> 0) Then
supplier.City = city
End If
If country Is Nothing AndAlso Not supplier.IsCountryNull() Then
supplier.SetCountryNull()
ElseIf (country IsNot Nothing AndAlso supplier.IsCountryNull) _
OrElse (Not supplier.IsCountryNull() AndAlso _
String.Compare(supplier.Country, country) <> 0) Then
supplier.Country = country
End If
借助此添加的代码,DAL s Update 方法仅针对地址相关值已更改的记录向数据库发送 UPDATE 语句。
或者,我们可以跟踪传入的地址字段与数据库数据之间是否存在任何差异,如果没有,只需绕过对 DAL 方法的 Update 调用。 如果使用 DB 直接方法,则此方法非常有效,因为 DB 直接方法未通过SuppliersRowRowState实例进行检查以确定是否实际需要数据库调用。
注意
每次调用该方法时 UpdateSupplierAddress ,都会调用数据库以检索有关更新记录的信息。 然后,如果数据有任何更改,则会对数据库进行另一次调用以更新表行。 可以通过创建接受UpdateSupplierAddress包含页面所有更改EmployeesDataTable的实例的方法重载BatchUpdate.aspx来优化此工作流。 然后,它可以对数据库进行一次调用,以便从 Suppliers 表中获取所有记录。 然后,可以枚举这两个结果集,并且只能更新发生更改的记录。
总结
本教程介绍了如何创建完全可编辑的 DataList,允许用户快速修改多个供应商的地址信息。 我们首先在 DataList s ItemTemplate中为供应商地址、城市和国家/地区值定义 TextBox Web 控件的编辑界面。 接下来,我们在 DataList 前面和下方添加了“更新所有”按钮。 用户进行更改并单击“全部更新”按钮之一后, DataListItem 将枚举 s 并调用 SuppliersBLL 类的方法 UpdateSupplierAddress 。
快乐编程!
关于作者
斯科特·米切尔,七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0。 可以通过 mitchell@4GuysFromRolla.com 联系到他。
特别感谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Zack Jones 和 Ken Pespisa。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请给我写信。mitchell@4GuysFromRolla.com