【问题标题】:ASP.NET: Postback processed without events being firedASP.NET:在未触发事件的情况下处理回发
【发布时间】:2010-01-28 10:09:30
【问题描述】:

我有一个带有动态创建的图像按钮的 GridView,单击时应该触发命令事件。事件处理基本上是有效的,除了第一次单击按钮。然后,回发被处理,但事件没有被触发。

我已尝试对此进行调试,在我看来,在第一次点击之前和之后执行的代码与任何其他点击完全相同。 (除了在第一次点击时,不调用事件处理程序。)

有一些特殊性:触发事件的按钮是通过数据绑定动态创建的,即数据绑定必须在页面生命周期中执行两次:一旦加载,为了使按钮存在(否则,事件可能根本不处理),并且在渲染之前一次,以便在事件处理后显示新数据。

我已阅读这些帖子,但它们与我的情况不符: ASP.NET LinkButton OnClick Event Is Not Working On Home Page, LinkButton not firing on production server, ASP.NET Click() event doesn't fire on second postback

到细节: GridView 在每一行中都包含图像按钮。按钮的图像是数据绑定的。行由 GridView.DataBind() 生成。为此,我使用了带有自定义 ItemTemplate 实现的 TemplateField。 ItemTemplate 的 InstantiateIn 方法创建 ImageButton 并为其分配相应的事件处理程序。此外,图像的 DataBinding 事件被分配了一个处理程序,该处理程序根据相应行的数据检索适当的图像。

GridView 放置在 UserControl 上。 UserControl 为 GridView 的事件定义事件处理程序。代码大致如下:

private DataTable dataTable = new DataTable();
protected SPGridView grid;

protected override void OnLoad(EventArgs e)
{
    DoDataBind(); // Creates the grid. This is essential in order for postback events to work.
}

protected override void Render(HtmlTextWriter writer)
{
    DoDataBind();
    base.Render(writer); // Renews the grid according to the latest changes
}

void ReadButton_Command(object sender, CommandEventArgs e)
{
    ImageButton button = (ImageButton)sender;
    GridViewRow viewRow = (GridViewRow)button.NamingContainer;
    int rowIndex = viewRow.RowIndex;

    // rowIndex is used to identify the row in which the button was clicked,
    // since the control.ID is equal for all rows.
    // [... some code to process the event ...]
}

private void DoDataBind()
{
    // [... Some code to fill the dataTable ...]

    grid.AutoGenerateColumns = false;

    grid.Columns.Clear();

    TemplateField templateField = new TemplateField();
    templateField.HeaderText = "";
    templateField.ItemTemplate = new MyItemTemplate(new CommandEventHandler(ReadButton_Command));
    grid.Columns.Add(templateField);

    grid.DataSource = this.dataTable.DefaultView;
    grid.DataBind();
}

private class MyItemTemplate : ITemplate
{
    private CommandEventHandler commandEventHandler;

    public MyItemTemplate(CommandEventHandler commandEventHandler)
    {
        this.commandEventHandler = commandEventHandler;
    }

    public void InstantiateIn(Control container)
    {
        ImageButton imageButton = new ImageButton();
        imageButton.ID = "btnRead";
        imageButton.Command += commandEventHandler;
        imageButton.DataBinding += new EventHandler(imageButton_DataBinding);
        container.Controls.Add(imageButton);
    }

    void imageButton_DataBinding(object sender, EventArgs e)
    {
        // Code to get image URL
    }
}

重复一遍:在每个生命周期中,首先执行 OnLoad,它会生成带有 ImageButtons 的 Grid。然后,处理事件。由于按钮在那里,事件通常有效。之后,调用 Render,它会根据新数据从头开始生成 Grid。这总是有效的,除了用户第一次点击图像按钮,虽然我已经断言当页面第一次发送给用户时也会生成网格和图像按钮。

希望有人能帮助我理解这一点或告诉我一个更好的解决方案。

【问题讨论】:

    标签: asp.net data-binding gridview event-handling postback


    【解决方案1】:

    这里有几个问题。第一,没有 IsPostBack 检查,这意味着您在每次加载时都要进行数据绑定……这势必会导致一些问题,包括未触发的事件。其次,您在每次加载时调用 DoDataBind() 两次,因为您在 OnLoad 和 Render 中调用它。为什么?

    绑定数据一次...然后再次对事件做出反应(如果需要)。

    其他问题...不要将事件绑定到模板字段中的 ImageButton。这通常是行不通的。使用 ItemCommand 事件和 CommandName/CommandArgument 值。

    最后...最后一个问题...您是否对整个页面在第一次加载和后续加载时呈现的 HTML 进行了比较(windiff 或其他工具)?它们完全一样吗?还是在控件名称或 PostBack 引用中存在细微差别?

    【讨论】:

    • 你好布莱恩,感谢您的回复。我绑定它两次的原因可以在上面找到:第一次是为了让控件在那里,以便可以触发事件;第二次为了根据事件处理程序所做的更改更新视图。如果我在回发后没有对 gridview 进行数据绑定,结果是空的(尽管 viewstate 已打开)=>??因此,我还必须在每次回发后对网格进行数据绑定。
    • CommandField 的问题是我无法对图像进行数据绑定 (?)。我必须为所有行定义一个相同的图像。相反,在我的情况下,我希望显示的图像取决于相应行的数据。 BoundField 反过来不提供命令事件。所以模板是我找到的唯一方法。如果我错了,请纠正我,我什至希望是这样!
    • 两个 HTML 文件之间的差异显示(仅)在 __REQUESTDIGEST、__EVENTVALIDATION 和当然在 __VIEWSTATE 中的差异
    【解决方案2】:

    我认为事件调度发生在页面加载之后。在这种情况下,它将尝试针对您的第一次数据绑定尝试创建的控件运行。此控件的 ID 与稍后重新创建时的 ID 不同。我猜 ASP.NET 正在尝试将传入的事件映射到控件,而不是找到控件,然后就是这样。

    我建议捕捉实际帖子中的内容。

    在事件绑定和动态创建的控件方面,ASP.NET 相当糟糕。玩得开心。

    【讨论】:

    • 谢谢你,弗兰克。有趣的是,它通常以这种方式工作。该问题仅在第一次回发后出现。
    • 我发现了另外两个有趣的细节: - 如果我使用普通的 Buttons 而不是 ImageButtons (在完全相同的地方,即仍然使用 MyItemTemplate 但实例化 Button 而不是 ImageButton “InstantiateIn”,它工作正常。 - 由于我的代码中的某些逻辑未在此处列出,因此在某些情况下跳过 OnLoad 中的“DoDataBind()”方法。仍然呈现控件由 Render() 中的 DoDataBind() 调用。但在 PostBack 循环之后,用户的点击交互将不会被处理。
    • 即:1. OnLoad() -> DoDataBind 已跳过。 1. Render() -> DoDataBind 执行,控件被渲染。 1. HTML 发送给客户端。 1. 客户点击 ImageButton。 1. PostBack 发送到服务器。 1. OnLoad() -> DoDataBind 执行。 1. 事件派发失败。 1. Render() -> DoDataBind 执行。 1. HTML 发送给客户端。 1. 客户点击 ImageButton。 1. PostBack 发送到服务器。 1. OnLoad() -> DoDataBind 执行。 1. 事件被触发。 ... => 当我断言 DoDataBind() 在将内容发送到客户端之前总是执行两次时,它起作用了!?
    • 这种双重绑定是典型的。需要第一个绑定来构建控制树,以便可以分派事件。一旦事件被分派,通常需要第二个数据绑定来说明更改。很烂吧?阅读 Control.EnsureChildControls(),这个过程会更有意义
    【解决方案3】:

    由于我认为这是部分答案,因此我以这种方式重新发布:

    • 如果我使用普通 Buttons 而不是 ImageButtons(在完全相同的位置,即仍然使用 MyItemTemplate 但在“InstantiateIn”中实例化 Button 而不是 ImageButton,它可以正常工作。

    • 如果我断言 DoDataBind() 在将内容发送到客户端之前总是执行两次,那么它与 ImageButtons 一起工作正常。

    还是不解,但无论如何……

    【讨论】:

      猜你喜欢
      • 2011-02-07
      • 2012-04-15
      • 1970-01-01
      • 1970-01-01
      • 2013-02-21
      • 1970-01-01
      • 2022-01-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多