I have a nested GridView that is placed inside a ListView. The GridView renders perfectly fine with all the entities that are bound inside its datasource.
Each record in my gridview has two buttons – ‘delete’ and ‘edit’. The issue that I am having is that the methods wired to each of these buttons never get fired.
I think the reason for this behaviour is because my ListView’s data binding happens only on the first page load, and not on every subsequent postback. As a result, when a postback happens, the events of the nested gridview are never wired up again – hence my methods are not getting fired.
Here is what my code [simplified] looks like:
<asp:ListView ID="uiListView" ... runat="server">
<LayoutTemplate>
...
</LayoutTemplate>
<ItemTemplate>
...
<asp:GridView ID="uiGridView"
OnRowDataBound="uiGridView_RowDataBound"
OnRowEditing="uiGridView_RowEditing"
OnRowDeleting="uiGridView_RowDeleting" runat="server">
<Columns>
...
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="uiEditRowButton" CausesValidation="false" CommandName="Edit" Text="Edit" runat="server" />
<asp:Button ID="uiRemoveRowButton" CausesValidation="false" CommandName="Delete" Text="Remove" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
...
</ItemTemplate>
</asp:ListView>
Code Behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) {
uiListView.ItemDataBound += new EventHandler<ListViewItemEventArgs>(uiListView_ItemDataBound);
uiListView.DataSource = ...;
uiListView.DataBind();
}
}
// Find a GridView and bind relevant data to it
private void uiListView_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem) {
ListViewDataItem listViewDataItem = (ListViewDataItem) e.Item;
GridView uiGridView = (GridView)listViewDataItem.FindControl("uiGridView");
...
uiGridView.DataSource = ...;
uiGridView.DataBind();
}
}
// For every row being bound to GridView, register the edit and delete
// buttons as postback controls
protected void uiGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Control uiEditButton = e.Row.FindControl("uiEditRowButton");
if (uiEditButton != null) {
ScriptManager.GetCurrent(Page).RegisterAsyncPostBackControl(uiEditButton);
}
Control uiRemoveRowButton = e.Row.FindControl("uiRemoveRowButton");
if (uiRemoveRowButton != null) {
ScriptManager.GetCurrent(Page).RegisterAsyncPostBackControl(uiRemoveRowButton);
}
}
}
// Method runs when a GridView's edit button is clicked
protected void uiGridView_RowEditing(object sender, GridViewEditEventArgs e)
{
Console.WriteLine('Editing Row...');
}
// Method runs when a GridView's delete button is clicked
protected void uiGridView_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
Console.WriteLine('Deleting Row');
}
I tried modifying the above code and removing the “!IsPostBack” clause, and the events actually got fired when a button inside the GridView was clicked. However, I do not feel comfortable doing a databind on every postback and think there should be a better solution than that.
I solved this issue but never updated this post. So for anyone interested, here is what the issue is – the wiring of the button events needs to be done in the RowCreated event handler and not the RowCommand event handler.
Why is that? Have a look at what MSDN has to say for each of the two events:
Now what is not clear from the msdn write-up is the follows:
OnRowCreated is triggered when a row is created by two different sources
So as a result, when a postback occurs, we need to bind the button events again on every row. However, because we are doing the wiring of events in the RowCommand, these methods are never being called again – because they only get fired on DataBind. What we need to do instead is wire the buttons inside the RowCreated method, which will be called when we first DataBind data to the GridView, and on every postback when rows are created from the ViewState of the GridView.