GridView 和 DataList 控件的可点击和双击行(ASP.NET 2.0)






4.56/5 (40投票s)
处理 GridView 和 DataList 控件的单击和双击事件。此外,使用 ClientScript.RegisterForEventValidation 注册事件并避免禁用事件验证。
引言
网上有许多资料介绍了如何使 GridView 的行在任何位置都可点击。也有一些资料介绍了如何使行可双击。但我还没有找到任何能同时处理这两种事件或处理 ASP.NET 2.0 中事件验证错误的资料。
在这里,我将介绍如何处理 GridView、DataList 和 ListBox 控件的单击和双击事件。我还将展示如何在不禁用事件验证的情况下处理它。
背景
在开发一个 ASP.NET 应用程序来替换一个旧的 Windows 应用程序时,我被要求尽可能地保留用户体验。原始应用程序允许用户选择一行并在工具栏上对该行执行操作。通过双击一行,用户可以打开另一个包含该行详细信息的表单。这种功能可以通过 GridView 的每一行上的两个单独的按钮轻松实现,但我需要比这更好的东西。
GridView 的单击和双击
创建一个新的 Web 应用程序,包含一个默认页面。暂时,将 EnableEventValidation="false" 添加到页面指令中。稍后我们将删除它。添加一个 GridView 并为其绑定一些数据。使用 GridView 上的“编辑列”选项添加两个 asp:ButtonField 控件。为这些按钮字段设置命令名称为 SingleClick 和 DoubleClick。(选择按钮也可以用于 SingleClick 事件,但我决定对两者都使用 asp:ButtonField。)
<Columns>
    <asp:ButtonField Text="SingleClick" CommandName="SingleClick" />
    <asp:ButtonField Text="DoubleClick" CommandName="DoubleClick" />
</Columns>
为 GridView 创建一个 RowCommand 事件,并使用一个 switch 块来捕获单独的事件。
在此演示中,此代码将记录触发事件的历史记录。SingleClick 命令还将设置行的 SelectedIndex。
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
    GridView _gridView = (GridView)sender;
    // Get the selected index and the command name
    int _selectedIndex = int.Parse(e.CommandArgument.ToString());
    string _commandName = e.CommandName;
    switch (_commandName)
    {
        case ("SingleClick"):
            _gridView.SelectedIndex = _selectedIndex;
            this.Message.Text += "Single clicked GridView row at index "
            + _selectedIndex.ToString() + "<br />";
        break;
        case ("DoubleClick"):
            this.Message.Text += "Double clicked GridView row at index "
            + _selectedIndex.ToString() + "<br />";
        break;
    }
}
创建一个 RowDataBound 事件来修改绑定的每一行。现在我们需要获取 SingleClick 按钮用于回发的客户端脚本,并将其分配给整行。
if (e.Row.RowType == DataControlRowType.DataRow)
{
    // Get the LinkButton control in the first cell
    LinkButton _singleClickButton = (LinkButton)e.Row.Cells[0].Controls[0];
    // Get the javascript which is assigned to this LinkButton
    string _jsSingle = 
    ClientScript.GetPostBackClientHyperlink(_singleClickButton, "");
    // Add this javascript to the onclick Attribute of the row
    e.Row.Attributes["onclick"] = _jsSingle;
}
我们也可以对 DoubleClick 按钮做同样的事情,但是这两者不能同时工作。
if (e.Row.RowType == DataControlRowType.DataRow)
{
    // Get the LinkButton control in the second cell
    LinkButton _doubleClickButton = (LinkButton)e.Row.Cells[1].Controls[0];
    // Get the javascript which is assigned to this LinkButton
    string _jsDouble = 
    ClientScript.GetPostBackClientHyperlink(_doubleClickButton, "");
    // Add this JavaScript to the ondblclick Attribute of the row
    e.Row.Attributes["ondblclick"] = _jsDouble;
}
如果我们同时实现这两个事件,我们将只能获得单击的功能。这是因为当用户开始双击时,第一次单击被视为一次单击,页面在第二次单击发生之前就已回发。JavaScript 的 setTimeout 方法可以用来为第一次单击设置一个超时,从而给用户完成双击的机会。
查看页面源代码,我们可以看到 <tr> 标签上的 onclick 事件
onclick="javascript:__doPostBack('GridView1$ctl02$ctl00','')"
添加一个 setTimeout 方法,并设置 300 毫秒的超时
onclick="javascript:setTimeout
        ("__doPostBack('GridView1$ctl02$ctl00','')", 300)"
所以整个 RowDataBound 代码如下
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // Get the LinkButton control in the first cell
        LinkButton _singleClickButton = (LinkButton)e.Row.Cells[0].Controls[0];
        // Get the javascript which is assigned to this LinkButton
        string _jsSingle = 
        ClientScript.GetPostBackClientHyperlink(_singleClickButton, "");
        // To prevent the first click from posting back immediately
        // (therefore giving the user a chance to double click) 
    // pause the postbackfor 300 milliseconds by 
        // wrapping the postback command in a setTimeout
        _jsSingle = _jsSingle.Insert(11, "setTimeout(\"");
        _jsSingle += "\", 300)";
        // Add this javascript to the onclick Attribute of the row
        e.Row.Attributes["onclick"] = _jsSingle;
        // Get the LinkButton control in the second cell
        LinkButton _doubleClickButton = (LinkButton)e.Row.Cells[1].Controls[0];
        // Get the javascript which is assigned to this LinkButton
        string _jsDouble = 
        ClientScript.GetPostBackClientHyperlink(_doubleClickButton, "");
        // Add this javascript to the ondblclick Attribute of the row
        e.Row.Attributes["ondblclick"] = _jsDouble;
    }
}
现在按钮不再需要可见,通过添加 Visible="false" 来隐藏它们
<Columns>
    <asp:ButtonField Text="SingleClick" CommandName="SingleClick" 
                            Visible="false" />
    <asp:ButtonField Text="DoubleClick" CommandName="DoubleClick" 
                            Visible="false" />
</Columns>
现在我们可以单击和双击一行,并在 RowCommand 代码中捕获相应的操作。
注册用于验证的回发或回调数据
一切正常,但请记住,我们在页面指令中添加了 EnableEventValidation="false"。这不是最安全的选择,所以我们应该删除它。这会导致在单击或双击行时出现“无效的回发或回调参数”错误。错误提示我们使用 ClientScriptManager.RegisterForEventValidation 方法来注册用于验证的回发或回调数据。(EventValidation 的作用已在其他地方有详细说明,超出本文范围。)
可以通过重写 Render 方法来调用 ClientScriptManager.RegisterForEventValidation。诀窍在于为 GridView 的每一行的两个按钮注册唯一的 ID。行的 UniqueID 由 GridViewRow.UniqueID 返回。第一个按钮的 UniqueID 可以通过在行的 UniqueID 后附加 “$ctl00” 来生成,第二个按钮则附加 “$ctl01”。
重写的 Render 方法如下
protected override void Render(HtmlTextWriter writer)
{
    foreach (GridViewRow r in GridView1.Rows)
    {
        if (r.RowType == DataControlRowType.DataRow)
        {
            Page.ClientScript.RegisterForEventValidation
                    (r.UniqueID + "$ctl00");
            Page.ClientScript.RegisterForEventValidation
                    (r.UniqueID + "$ctl01");
        }
    }
    base.Render(writer);
}
现在我们不会再遇到“无效的回发或回调参数”错误了。
其他控件
在源代码中,我还演示了如何使用 DataList 和 ListBox 控件实现此功能。代码非常相似,因此我在此不展示。
DataList 的单击和双击
对于 DataList,ItemTemplate 的内容被包裹在一个 asp:Panel 中,提供了一个可以为其分配 onclick 和 ondblclick 事件的对象。
ListBox 的单击和双击
对于 ListBox,没有行或项事件,但可以将 onclick 和 ondblclick 属性添加到控件中。
在 SelectedIndexChanged 事件处理程序中,Request.Form["__EventArgument"] 将返回命令名称。
结论
通过仔细查看 ASP.NET 生成的 JavaScript,我们可以扩展 GridView 和 DataList 控件的功能。为了保持应用程序安全,如果可能,应避免使用 EnableEventValidation="false"。一旦您理解了 EventValidation 的工作原理,就可以使用 ClientScript.RegisterForEventValidation 来确保您的代码按预期工作。
历史
- v1.0 - 2006 年 9 月 23 日
- v1.1 - 2006 年 9 月 25 日
- v1.2 - 2006 年 11 月 12 日
 在演示中添加了 ListBox 控件
- v1.3 - 2006 年 12 月 20 日
 添加了 VB.NET 演示版本到下载文件中


