Search code examples
asp.netvb.netdatatablerepeater

The last item in an ASP.NET repeater is being overlooked by the codebehind


I have a fairly straightforward repeater fed by a DataTable. There are elements in the ItemTemplate set to visible=false. In the ItemDataBound event I toggle visibility and such based on what's in the DataTable. All works well enough except that the final row of the repeater gets its base data but not any of the changes from the ItemDataBound event. I can step through execution and watch them get changed in the codebehind, but those changes are not reflected in the ASPX. This only happens for the final row (or only row if only one record is returned). If I return four rows, I get three rows of correct buttons and such, and a fourth row with only unmodified base data.

I have created a new repeater on the page, and then stripped it down to the very basics. I have played with the ClientIdMode and the .NET framework version. I have triple-checked the tag formatting, removed anything non-essential and rebooted every computer in the building twice. I have turned ViewState on and off, I have removed IsPostBack checks, and I have renamed the repeater. I am not using nested repeaters (elsewhere in the project, but not on this page), I am not using validation, and I have removed all my CommandArg stuff.

My ASPX:

<asp:repeater ID="rptrTEST" runat="server" Visible="true">
    <ItemTemplate>
        <span ID="TESTspanParticipantCSI" visible="true" runat="server">
            CSI: <%#Eval("MoodleID")%>
        </span>
        <br />
            <img src="../images/projectname/btn-add-subscriber-20.png"
                class="TESTbtnSubscriber TESTbtnParticipantSubscribe"
                ID="TESTbtnParticipantSubscribe"
                alt="Subscribe participant cell phone"
                visible="false"
                runat="server" />
            <asp:ImageButton
                ID="TESTbtnParticipantPause"
                CssClass="TESTbtnSubscriber TESTbtnPause TESTbtnParticipantPause"
                Visible="false"
                ImageURL="~/images/projectname/btn-pause-subscriber-20.png"
                runat="server" />
            <asp:ImageButton
                ID="TESTbtnParticipantPlay"
                CssClass="TESTbtnSubscriber TESTbtnPlay TESTbtnParticipantPlay"
                Visible="false"
                ImageURL="~/images/projectname/btn-play-subscriber-20.png"
                runat="server" />
            <asp:ImageButton
                ID="TESTbtnParticipantUnsubscribe"
                CssClass="TESTbtnParticipantUnsubscribe"
                Visible="false"
                ImageURL="../images/projectname/btn-delete-subscriber-20.png"
                runat="server" />
        <br />
    </ItemTemplate>
</asp:repeater>

My VB:

Protected Sub rptrTEST_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptrTEST.ItemDataBound
    Dim objCSIs As New Reg_DB.clsSubscribers
    Dim dtCSIs As Data.DataTable = objCSIs.GetAllCSIs()
    Dim y As Integer = 0

    For Each rptItem As RepeaterItem In rptrTEST.Items
        Dim spanParticipantCSI As New HtmlGenericControl
        Dim btnParticipantSubscribe As HtmlImage = DirectCast(rptrTEST.Items(y).FindControl("TESTbtnParticipantSubscribe"), HtmlImage)
        Dim btnParticipantUnsubscribe As ImageButton = DirectCast(rptrTEST.Items(y).FindControl("TESTbtnParticipantUnsubscribe"), ImageButton)
        Dim btnParticipantPause As ImageButton = DirectCast(rptrTEST.Items(y).FindControl("TESTbtnParticipantPause"), ImageButton)
        Dim btnParticipantPlay As ImageButton = DirectCast(rptrTEST.Items(y).FindControl("TESTbtnParticipantPlay"), ImageButton)
        If dtSubscribersCooked.Rows(y)("ParticipantSubStatus") = "NONE" then
            spanParticipantCSI.InnerHtml = "subscribed"
            btnParticipantUnsubscribe.Visible = true
            btnParticipantPause.Visible = true
            btnParticipantSubscribe.Visible = false
            spanParticipantCSI.InnerHtml = "subscribed"
            btnParticipantPlay.Visible = false
        else
            spanParticipantCSI.InnerHtml = "whatever"
            btnParticipantUnsubscribe.Visible = true
            btnParticipantPause.Visible = true
            btnParticipantSubscribe.Visible = false
            spanParticipantCSI.InnerHtml = "sure"
            btnParticipantPlay.Visible = false
        end if
        y=y+1
    next
End Sub

So do I need an old priest and a young priest, or will this require shamans instead?


Solution

  • This is because the count of the items in the repeater isn't what you are expecting. When you loop through each item in the repeater's collection, it only knows of the items that have already been bound. Since your last item hasn't been bound yet (it is currently being bound), it doesn't see that item.

    What you need to do is get rid of that For Each loop entirely. Then just edit the RepeaterItem that is currently being bound. Notice how I find each control for the current item: e.Item.FindControl()

    Protected Sub rptrTEST_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptrTEST.ItemDataBound
        Dim objCSIs As New Reg_DB.clsSubscribers
        Dim dtCSIs As Data.DataTable = objCSIs.GetAllCSIs()    
    
        Dim spanParticipantCSI As New HtmlGenericControl
        Dim btnParticipantSubscribe As HtmlImage = DirectCast(e.Item.FindControl("TESTbtnParticipantSubscribe"), HtmlImage)
        Dim btnParticipantUnsubscribe As ImageButton = DirectCast(e.Item.FindControl("TESTbtnParticipantUnsubscribe"), ImageButton)
        Dim btnParticipantPause As ImageButton = DirectCast(e.Item.FindControl("TESTbtnParticipantPause"), ImageButton)
        Dim btnParticipantPlay As ImageButton = DirectCast(e.Item.FindControl("TESTbtnParticipantPlay"), ImageButton)
    
        If dtSubscribersCooked.Rows(y)("ParticipantSubStatus") = "NONE" then
            spanParticipantCSI.InnerHtml = "subscribed"
            btnParticipantUnsubscribe.Visible = true
            btnParticipantPause.Visible = true
            btnParticipantSubscribe.Visible = false
            spanParticipantCSI.InnerHtml = "subscribed"
            btnParticipantPlay.Visible = false
        else
            spanParticipantCSI.InnerHtml = "whatever"
            btnParticipantUnsubscribe.Visible = true
            btnParticipantPause.Visible = true
            btnParticipantSubscribe.Visible = false
            spanParticipantCSI.InnerHtml = "sure"
            btnParticipantPlay.Visible = false
        end if
    
    End Sub
    

    I'm not sure what dtSubscribersCooked is or where it is declared. If that is a global variable or something, then an option for you may be to bind ParticipantSubStatus to a HiddenField on your repeater to get it's value on each ItemDataBound.