views:

791

answers:

5

This is a absurdly common issue and having exhausted all of the obvious solutions, I'm hoping SO can offer me some input... I have a UserControl inside a page which contains a repeater housing several controls that cause postback. Trouble is, all of the controls inside of the repeater never hit their event handlers when they postback, but controls outside of the repeater (still in the UC) are correctly handled. I already made sure my controls weren't being regenerated due to a missing if(!IsPostBack) and I verified that Request.Form["__EVENTTARGET"] contained the correct control ID in the Page_Load event. I attempted to reproduce the symptoms in a separate project and it worked as it should.

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="NoteListControl.ascx.cs"
    Inherits="SantekGBS.Web.UserControls.NoteListControl" %>

<asp:UpdatePanel ID="upNotes" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
        <div class="NoteList" id="divNoteList" runat="server">
            <asp:Repeater ID="repNotes" runat="server">
                <HeaderTemplate>
                    <table width="98%" cellpadding="3" cellspacing="0">
                </HeaderTemplate>
                <ItemTemplate>
                    <tr class="repeaterItemRow">
                        <asp:ImageButton ID="ImageButton1" runat="server" ImageUrl="~/Content/images/DeleteIcon.gif"
                            OnClick="ibRemove_Click" CommandArgument='<%# Container.ItemIndex %>' CommandName='<%# Eval("ID") %>'
                            CausesValidation="false" AlternateText="Delete" />
                        <%# Eval("Text") %></td>
                    </tr>
                </ItemTemplate>
                <FooterTemplate>
                    </table>
                </FooterTemplate>
            </asp:Repeater>
            <asp:PlaceHolder ID="phNoNotes" runat="server" Visible="false">
                <div class="statusMesssage">
                    No notes to display.
                </div>
            </asp:PlaceHolder>
        </div>
    </ContentTemplate>
</asp:UpdatePanel>


public partial class NoteListControl : UserControl
{
    [Ninject.Inject]
    public IUserManager UserManager { get; set; }

    protected List<Note> Notes
    {
        get
        {
            if (ViewState["NoteList"] != null)
                return (List<Note>)ViewState["NoteList"];
            return null;
        }
        set { ViewState["NoteList"] = value; }
    }

    public event EventHandler<NoteEventArgs> NoteAdded;
    public event EventHandler<NoteEventArgs> NoteDeleted;
    public event EventHandler<NoteEventArgs> NoteChanged;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            UtilityManager.FillPriorityListControl(ddlPriority, false);
        }
    }

    protected void ibRemove_Click(object sender, ImageClickEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("ibRemove POSTBACK"); // This is NEVER hit
    }

    public void Fill(List<Note> notes)
    {
        Notes = notes;
        RefreshRepeater();
    }

    private void RefreshRepeater()
    {
        if (Notes != null && Notes.Any())
        {
            var sorted = Notes.OrderByDescending(n => n.Timestamp);
            Notes = new List<Note>();
            Notes.AddRange(sorted);
            repNotes.Visible = true;
            phNoNotes.Visible = false;
            repNotes.DataSource = Notes;
            repNotes.DataBind();
        }
        else
        {
            repNotes.Visible = false;
            phNoNotes.Visible = true;
        }
    }
}

public class NoteEventArgs : EventArgs
{
    public Note Note { get; set; }
    public NoteEventArgs()
    { }
    public NoteEventArgs(Note note)
    {
        this.Note = note;
    }
}

The code is intentionally missing functionality so just disregard that fact.

+1  A: 

I found a missing td-tag in the Itemtemplate, sometimes when DOM is incorrect, the updatapanel do strange things.

Henrik Adolfsson
Good catch, but you can actually disregard that. The markup I'm showing isn't even the same as the production code. I created that <ItemTemplate> just to show the effect. All controls inside the ItemTemplate behave the same way.
Nathan Taylor
A: 

I asked a friend here at work (I must admit I didn't knew the answer), and he says your problem is in the ibRemove_Click declaration. It should be public and not protected to work.
As the buttons "are not real class objects" instead they are created on the flight and by some reason they don't see the protected elements, only the public. I promise to study this. Looks interesting. :D

Limo Wan Kenobi
This does not seem to make any difference. It's worth noting that at one point in time this code worked perfectly. I'm not sure what changed to affect that.
Nathan Taylor
protected and public both work, private will not
Bob
@Bob that has always been my understanding as well.
Nathan Taylor
Googleing around your problem I found this: "I was just working on this problem and found that if you don't include EnsureChildControls in OnLoad then the event doesn't fire." Here: http://www.eggheadcafe.com/community/aspnet/2/10012342/create-image-button-event.aspx You could try it, maybe It helps.
Limo Wan Kenobi
+1  A: 

Your edited code has residual CommandArgument and CommandName properties; are you actually handling the Repeater.ItemCommand event?

If so, and if your page calls the control's Fill method on postbacks, that would explain it.

This classic ASP.NET hair-tearing problem is explained in these posts: A Stumper of an ASP.NET Question and A Stumper of an ASP.NET Question: SOLVED!

The explanation is a little mind-bending, but the crux of it is that Repeater.DataBind interferes with ASP.NET's ability to determine which repeater button caused a postback.

Jeff Sternal
Although I am using the CommandArgument and CommandName arguments I'm not bubbling events through Repeater.ItemCommand. I'm using the properties to store information which I access in the standard button click event.
Nathan Taylor
i think the link should be http://scottonwriting.net/sowBlog/archive/0000/00/00/162929.aspx
darasd
@darasd - thanks for the link correction!
Jeff Sternal
+1  A: 

Just about EVERY time I run into this problem it's because DataBind() is being called when it shouldn't be. This will kill most events from controls inside a repeater. I see you have an !IsPostBack check in your Page_Load... so that's a start. But try putting a breakpoint on repNotes.DataBind() and see if it's getting called when you don't expect it.

Does it work OK outside of an UpdatePanel?

Bryan
DataBind() is only being called on the initial page load and after specified control events- none of which are being fired as they should. The UpdatePanel does not seem to be related to the problem. I have another form in my app that also used to work that is exhibiting similar behavior as well, when the form posts back the repeater empties itself as it does here. Very strange behavior.
Nathan Taylor
You haven't disabled ViewState for any of these controls have you? You have "another form"... I assume that's a different .aspx page altogether? You don't have an extra form tag in this page?
Bryan
A: 

Where you able to find why the event was not fired. I have the same issue - Repeater in a User Control and the link button on the repeater wont fire the events.

Vivek
I moved by repeater's databind method to page load and it has started working. Only issue in certain circumstance it binds twice - working on it as to how avoid in certain post back scenarios.Cheers
Vivek