65.9K
CodeProject 正在变化。 阅读更多。
Home

从客户端脚本访问 ASP.NET 模板化控件的数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.06/5 (9投票s)

2005年7月15日

CPOL

5分钟阅读

viewsIcon

70620

downloadIcon

809

本文演示了一种从客户端脚本轻松访问模板化控件(Repeater、DataList、DataGrid)数据的方法。

Sample Image - ControlClientScript.jpg

引言

本文演示了一种从客户端脚本轻松访问模板化控件(RepeaterDataListDataGrid)数据的方法。这将使我们能够将这些数据用于客户端操作,如计算和验证。

背景

模板化控件是一种特殊的数据绑定服务器控件,在渲染列表式数据时具有极大的灵活性。在设计时,模板化控件允许您结合使用 HTML 和服务器控件来为单个列表项的字段设计自定义布局。在运行时,它会绑定到数据源,遍历绑定的项,并根据您设计的模板生成图形元素。

.NET 框架 1.0/1.1 提供了三个模板化控件:RepeaterDataListDataGrid。您还可以通过实现 INamingContainer 接口来构建自己的模板化控件。

使用模板化控件时可能遇到的一个挑战是如何从客户端脚本访问生成的子控件中的数据,因为客户端不理解模板,它只理解单个控件的名称,并且很难在运行时猜测每个子控件的名称。

问题所在

我将用一个简单的例子来解释我的想法。假设我们有一个数据库表,用于存储特定时期内的员工费用。该表具有以下架构

ID int
ExpenseType varchar
ExpenseAmount decimal

我们希望员工使用 DataGrid 控件输入每种费用类型的花费金额。当员工更新每行的金额时,需要显示他输入的总金额,并验证此总金额是否不超过 1000。

如果我们决定逐行更新,这些要求就很容易实现。这意味着,当员工更新每一行时,表单必须进行回发到服务器以计算新的总计,并应用验证规则。

客户端方法

我提出的方法有几个好处,因为它节省了服务器和网络资源,并提供了更具交互性的用户界面。

使用的 DataGrid 结构如下

<asp:datagrid id="dgExpenses" runat="server" AutoGenerateColumns="False">
<Columns>
  <asp:BoundColumn DataField="ExpenseType" HeaderText="Expense Type">
  </asp:BoundColumn>
  <asp:TemplateColumn HeaderText="Expense Amount">
   <ItemTemplate>
    <asp:TextBox Text='<%# DataBinder.Eval(Container.DataItem,"ExpenseAmount")%>' 
    Runat="server" ID="txtExpAmount"></asp:TextBox>
   </ItemTemplate>
  </asp:TemplateColumn>
 </Columns>
</asp:datagrid>

填充 DataGrid 的代码很简单

SqlConnection conn = new 
     SqlConnection("server=localhost;database=pubs;uid=sa;pwd=");
SqlCommand cmd = new SqlCommand("select * from timesheet", conn);
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
dgExpenses.DataSource = rdr;
dgExpenses.DataBind();
rdr.Close();

为了将数据暴露给客户端脚本,我使用一个隐藏字段来存储 ExpenseAmount 字段的初始值,作为逗号分隔值的字符串。

hdnRowArr.Value = "";
string expAmount;
foreach(DataGridItem dgi in dgExpenses.Items)
{
    if(dgi.ItemType == ListItemType.Item || 
       dgi.ItemType == ListItemType.AlternatingItem)
    {
        expAmount = ((TextBox)dgi.Cells[1].Controls[1]).Text;
        hdnRowArr.Value += expAmount + ",";
    }
}
hdnRowArr.Value = hdnRowArr.Value.TrimEnd(',');

在客户端,我将逗号分隔的值拆分为一个数组。使用数组可以更轻松地在用户进行更改时更新值。它还简化了总计的计算。我使用以下函数来填充数组。然后计算初始总计以更新 UI。此函数在 Body 的 OnLoad 事件中调用。

function refreshArray()
{
    var commaSeparated = document.getElementById("hdnRowArr").value;
    arrRows = commaSeparated.split(",");
    sumArray();
}

现在我们需要一种方法来检测用户所做的任何更改,更新数组,并重新计算总计。我将以下代码添加到 DataGridItemDataBound 事件中。

if(e.Item.ItemType == ListItemType.Item || 
     e.Item.ItemType == ListItemType.AlternatingItem)
{
    int rowIndex = e.Item.ItemIndex;
    TextBox txtExpAmount = (TextBox)e.Item.Cells[1].Controls[1];
    string evtHandler = "updateValue(this," + rowIndex.ToString() + ")";
    txtExpAmount.Attributes.Add("onblur", evtHandler);
}

此代码将为 ExpenseAmount 模板列中的每个 TextBox 添加一个 onblur 事件处理程序。事件将由 updateValue 客户端函数处理。我将子控件的引用和包含该控件的行的索引传递给该函数。索引用于定位与 DataGrid 行对应的数组元素。

function updateValue(field, rowNum)
{
    arrRows[rowNum] = field.value;
    sumArray();
}

通过此函数完成总计的计算和 UI 的更新

function sumArray()
{
    var sum = 0;
    var expAmount;
    for(var i = 0;i < arrRows.length; i++)
    {
        expAmount = parseFloat(arrRows[i]);
        if(!isNaN(expAmount))
            sum += expAmount;
    }
    document.getElementById("txtTotalExp").value = sum;
}

最后,我们可以将任何验证器与 txtTotalExp TextBox 关联起来。我们必须将 TextBox 的“ReadOnly”属性设置为“true”,以防止用户手动更新总计。因此,现在随着 ExpenseAmount TextBoxes 的值发生每次更改,总计将自动重新计算。

使用代码

为了将此方法应用于任何模板化控件,您可以按照以下步骤为要从客户端访问的每个列进行操作

  1. 添加一个隐藏字段,并用从数据源检索到的初始数据填充它。将数据作为逗号分隔的值传递给该字段。
  2. 在表单中添加一个 Array 变量以及相应的 refreshArrayupdateValuesumArray JavaScript 函数。您可以将 sumArray 函数替换为执行除计算总计之外的任何其他操作的函数。
  3. 在 Body 的 OnLoad 事件中,添加对 refreshArray 函数的调用。
  4. 使用模板化控件的 ItemDataBound 事件来添加对 updateValue 函数的调用,以处理子控件的客户端事件。
  5. 添加一个 LabelTextBox 来显示客户端操作的结果。然后,您可以添加一个合适的验证器并将其与 TextBox 关联起来。

关注点

  • 客户端方法可以扩展到满足各种模板化控件的验证需求。例如,如果您的 DataGrid 具有包含 CheckBox 的模板列(如下图所示),并且您想限制可以选中的 CheckBox 的最大或最小数量,则可以通过应用此处介绍的相同概念轻松实现。此示例包含在示例代码中。

    Sample screenshot

  • 需要注意的是,在客户端执行验证或其他操作的目的不是将数据处理的一部分从服务器移动到客户端,而是避免在用户输入数据时进行重复的服务器往返。因此,在我这里的示例中,当用户决定提交他们输入的数据时,我应该让服务器重新计算和验证总计,而不是依赖客户端计算的结果,因为这可能会被恶意用户篡改。
© . All rights reserved.