作者 :斯科特·米切尔
了解如何创建完全可编辑的 DataList,其中所有项都处于编辑模式,并且可以通过单击页面上的“全部更新”按钮保存其值。
介绍
在 前面的教程中,我们讲解了如何创建项目级 DataList。 与标准可编辑 GridView 一样,DataList 中的每个项都包含一个“编辑”按钮,单击该按钮会使项目可编辑。 虽然此项级编辑适用于仅偶尔更新的数据,但某些用例方案要求用户编辑许多记录。 如果用户需要编辑数十条记录,并被迫单击“编辑”,进行更改,然后单击每个记录的“更新”,则单击次数可能会阻碍她的工作效率。 在这种情况下,更好的选择是提供完全可编辑的 DataList,其中 所有 项都处于编辑模式,并且可以通过单击页面上的“全部更新”按钮来编辑其值(请参阅图 1)。
图 1:可以修改完全可编辑 DataList 中的每个项(单击以查看全尺寸图像)
本教程介绍如何让用户使用完全可编辑的 DataList 更新供应商地址信息。
步骤 1:在 DataList s ItemTemplate 中创建可编辑用户界面
在前面的教程中,我们在创建标准项级可编辑 DataList 时使用了两个模板:
-
ItemTemplate包含只读用户界面(用于显示每个产品名称和价格的标签 Web 控件)。 -
EditItemTemplate包含编辑模式用户界面(两个 TextBox Web 控件)。
DataList 的EditItemIndex属性决定如果有内容呈现,则使用DataListItem和EditItemTemplate。 具体而言,使用 DataListItem 来呈现其 ItemIndex 值与 DataList 的 EditItemIndex 属性匹配的 EditItemTemplate 。 该模型在一次只能编辑一个项目时效果很好,但在创建完全可编辑的 DataList 时会出现问题。
对于完全可编辑的 DataList,我们希望 所有DataListItem s 都使用可编辑接口进行呈现。 实现此目的的最简单方法是在 中 ItemTemplate定义可编辑接口。 要修改供应商的地址信息,可编辑接口以文本形式显示供应商名称,并包含用于输入地址、城市和国家/地区的文本框。
首先打开 BatchUpdate.aspx 页面,添加 DataList 控件,并将其属性设置为 IDSuppliers。 从 DataList 的智能标记中,选择添加名为 SuppliersDataSource 的新 ObjectDataSource 控件。
图 2:创建名为 SuppliersDataSource 的新 ObjectDataSource (单击可查看全尺寸图像)
将 ObjectDataSource 配置为使用 SuppliersBLL 类 s GetSuppliers() 方法检索数据(请参阅图 3)。 与前面的教程一样,我们将直接使用业务逻辑层,而不是通过 ObjectDataSource 更新供应商信息。 因此,将“更新”选项卡中的下拉列表设置为“无”(请参阅图 4)。
图 3:使用 GetSuppliers() 方法检索供应商信息(单击以查看全尺寸图像)
图 4:在“更新”选项卡中将 Drop-Down 列表设置为“无”(单击以查看全尺寸图像)
完成向导后,Visual Studio 自动生成 DataList, ItemTemplate 以显示标签 Web 控件中数据源返回的每个数据字段。 我们需要修改此模板,以便它改为提供编辑界面。
ItemTemplate可以使用 DataList 智能标记中的“编辑模板”选项或通过声明性语法直接通过设计器自定义。
花点时间创建一个编辑界面,该界面将供应商的名称显示为文本,但地址、城市和国家/地区等内容则显示在文本框中。 进行这些更改后,页面的声明性语法应如下所示:
<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类中,并配置为使用与ProductPropertyLabel和ProductPropertyValue CSS 类相同的样式设置。
.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 void UpdateAll1_Click(object sender, EventArgs e)
{
UpdateAllSupplierAddresses();
}
protected void UpdateAll2_Click(object sender, EventArgs e)
{
UpdateAllSupplierAddresses();
}
private void UpdateAllSupplierAddresses()
{
// TODO: Write code to update _all_ of the supplier addresses in the DataList
}
图 6 显示添加“全部更新”按钮后的页面。
图 6:已将两个更新所有按钮添加到页面(单击以查看全尺寸图像)
步骤 3:更新所有供应商地址信息
通过使所有 DataList 项目显示编辑界面,并添加“全部更新”按钮,现在剩下的任务就是编写代码以执行批量更新。 具体而言,我们需要循环访问 DataList 项,并为每个项调用 SuppliersBLL 类 s UpdateSupplierAddress 方法。
可以通过 DataList 属性访问构成 DataList 的DataListItem实例集合Items。 通过引用DataListItem,我们可以从SupplierID集合中获取相应的DataKeys,并在ItemTemplate中以编程方式引用 TextBox Web 控件,如以下代码所示:
private void UpdateAllSupplierAddresses()
{
// Create an instance of the SuppliersBLL class
SuppliersBLL suppliersAPI = new SuppliersBLL();
// Iterate through the DataList's items
foreach (DataListItem item in Suppliers.Items)
{
// Get the supplierID from the DataKeys collection
int supplierID = Convert.ToInt32(Suppliers.DataKeys[item.ItemIndex]);
// Read in the user-entered values
TextBox address = (TextBox)item.FindControl("Address");
TextBox city = (TextBox)item.FindControl("City");
TextBox country = (TextBox)item.FindControl("Country");
string addressValue = null, cityValue = null, countryValue = null;
if (address.Text.Trim().Length > 0)
addressValue = address.Text.Trim();
if (city.Text.Trim().Length > 0)
cityValue = city.Text.Trim();
if (country.Text.Trim().Length > 0)
countryValue = country.Text.Trim();
// Call the SuppliersBLL class's UpdateSupplierAddress method
suppliersAPI.UpdateSupplierAddress
(supplierID, addressValue, cityValue, countryValue);
}
}
当用户单击“全部更新”按钮之一时,该方法 UpdateAllSupplierAddresses 会遍历 DataListItem DataList 中的每一个 Suppliers,并调用 SuppliersBLL 类的 UpdateSupplierAddress 方法,传入相应的值。 地址、城市或国家/地区的未输入值将作为值 Nothing 至 UpdateSupplierAddress (而不是空白字符串)传递,从而导致数据库对底层记录字段进行 NULL 。
注释
作为增强功能,你可能希望向页面添加状态标签 Web 控件,该页面在执行批处理更新后提供一些确认消息。
仅更新已修改的地址
本教程中使用的批处理更新算法调用 UpdateSupplierAddress DataList 中每个 供应商的方法,无论其地址信息是否已更改。 虽然此类盲目更新通常不是性能问题,但如果正在审核数据库表的更改,它们可能会导致多余的记录。 例如,如果使用触发器将所有 UPDATE 记录到 Suppliers 审核表中,则每当用户单击“全部更新”按钮时,系统中的每个供应商都会创建新的审核记录,而不管用户是否进行任何更改。
ADO.NET DataTable 和 DataAdapter 类专为支持批量更新而设计,仅修改、删除和新增记录时才进行数据库通信。 DataTable 中的每个行都有一个 RowState 属性 ,该属性指示该行是否已添加到 DataTable、从其中删除、修改或保持不变。 最初填充 DataTable 时,所有行都标记为未更改。 更改任意一列的值都会将该行标记为已修改。
在类中SuppliersBLL,我们先在单个供应商记录SuppliersDataTable中读取指定的供应商地址信息,然后使用以下代码设置Address和CityCountry列值:
public bool UpdateSupplierAddress
(int supplierID, string address, string city, string country)
{
Northwind.SuppliersDataTable suppliers =
Adapter.GetSupplierBySupplierID(supplierID);
if (suppliers.Count == 0)
// no matching record found, return false
return false;
else
{
Northwind.SuppliersRow supplier = suppliers[0];
if (address == null)
supplier.SetAddressNull();
else
supplier.Address = address;
if (city == null)
supplier.SetCityNull();
else
supplier.City = city;
if (country == null)
supplier.SetCountryNull();
else
supplier.Country = country;
// Update the supplier Address-related information
int rowsAffected = Adapter.Update(supplier);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
}
无论值是否已更改,此代码都会将传入的地址、城市和国家/地区值直接赋给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 == null && !supplier.IsAddressNull())
supplier.SetAddressNull();
else if ((address != null && supplier.IsAddressNull()) ||
(!supplier.IsAddressNull() &&
string.Compare(supplier.Address, address) != 0))
supplier.Address = address;
if (city == null && !supplier.IsCityNull())
supplier.SetCityNull();
else if ((city != null && supplier.IsCityNull()) ||
(!supplier.IsCityNull() && string.Compare(supplier.City, city) != 0))
supplier.City = city;
if (country == null && !supplier.IsCountryNull())
supplier.SetCountryNull();
else if ((country != null && supplier.IsCountryNull()) ||
(!supplier.IsCountryNull() &&
string.Compare(supplier.Country, country) != 0))
supplier.Country = country;
借助此添加的代码,DAL s Update 方法仅针对地址相关值已更改的记录向数据库发送 UPDATE 语句。
或者,我们可以跟踪传入的地址字段与数据库数据之间是否存在任何差异,如果没有,只需绕过对 DAL 方法的 Update 调用。 如果使用 DB 直接方法,则此方法非常有效,因为 DB 直接方法没有接收一个可以检查的SuppliersRow实例来确定是否实际需要数据库调用。
注释
每次调用该方法时 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 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 《Sams Teach Yourself ASP.NET 2.0 in 24 Hours》。 可以通过 mitchell@4GuysFromRolla.com 联系到他。
特别致谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Zack Jones 和 Ken Pespisa。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请给我写信。mitchell@4GuysFromRolla.com