我有一个带有动态创建的图像按钮的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()生成。为此,我使用TemplateField和自定义ItemTemplate实现。 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。这总是有效的,除了用户第一次点击图像按钮之外,尽管我已断言在第一次将页面发送给用户时也会生成网格和图像按钮。
希望有人能帮助我理解这一点,或者告诉我一个更好的解决方案。
答案 0 :(得分:3)
这里有几个问题。第一,没有IsPostBack检查,这意味着你在每次加载时都是数据绑定...这必然会导致一些问题,包括没有触发的事件。其次,你在每次加载时调用DoDataBind()两次,因为你在OnLoad和Render中调用它。为什么呢?
将数据绑定在... ...然后再次响应事件(如果需要)。
其他问题...不要在模板字段中将事件绑定到ImageButton。这通常不起作用。使用ItemCommand事件和CommandName / CommandArgument值。
最后......最后一个问题...你是否已经对第一次加载时整个页面呈现的HTML进行了比较(windiff或其他工具),然后是后续加载?它们完全一样吗?或者在控件名称或PostBack引用中有轻微差异吗?
答案 1 :(得分:1)
我认为事件调度是在页面加载后发生的。在这种情况下,它将尝试针对您的第一次数据绑定尝试创建的控件运行。此控件具有与以后重新创建时不同的ID。我猜ASP.NET试图将传入的事件映射到控件,而不是找到一个控件,然后就是这样。
我建议抓拍实际帖子中的内容。
ASP.NET在事件绑定和动态创建的控件方面非常糟糕。玩得开心。
答案 2 :(得分:0)
由于在我看来这是一个部分答案,我会以这种方式重新发布:
如果我使用普通按钮代替ImageButtons(在完全相同的位置,即仍然使用MyItemTemplate但在“InstantiateIn”中实例化Button而不是ImageButton),它可以正常工作。
如果我断言DoDataBind()在将内容发送到客户端之前总是执行两次,那么它对ImageButtons工作正常。
仍然感到困惑,但不管......