0
votes

I'm having a bit of trouble with ASP Repeaters and trying to sort the data.

So on Page_Load I obtain the datasource as below...

    Protected Overloads Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
        If Not (Page.IsPostBack) Then

            'No need to re-load Occasion because it's done in the PreLoad event
            Me.Header.Title = "OCP - " + Occasion.Checklist.name

            pnlStatistics.Visible = Not (Occasion.isClosed)
            pnlClose.Visible = SecurityHelper.HasRole(SecurityMatchOption.AtLeast, SecurityRole.Manager, _relevantTeam) And Not (Occasion.isClosed)

            'initially assume checklist can be closed. any un-signed off task in the item_databound event will disable it.
            btnClose.Enabled = True

            'Fix Issue 63: rptTask.DataSource = _db.vw_tasklists.Where(Function(o) o.occasionId = Occasion.id).ToList()
            rptTask.DataSource = _db.vw_tasklists.Where(Function(o) o.occasionId = Occasion.id).OrderBy(Function(t) t.taskDueDate).ThenBy(Function(t) t.taskDueTime)

However within ItemDataBound we recalculate the task duedate.

    Private Sub rptTask_ItemDataBound(sender As Object, e As RepeaterItemEventArgs) Handles rptTask.ItemDataBound

        ' Get the data relating to this task 'row'
        Dim t = CType(e.Item.DataItem, vw_tasklist)

        If e.Item.ItemType = ListItemType.Header Then
            Dim thDueDate = CType(e.Item.FindControl("thDueDate"), HtmlTableCell)

            thDueDate.Visible = Not (Occasion.isClosed)
        End If

        'securable buttons

        If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then

            ' Dynamically create a span element named TASKnnnn, which will be referenced from
            ' 'child page' links back to this page in order to vertically reposition at the selected task
            Dim span = New HtmlGenericControl("span")
            span.ID = "TASK" & t.taskId
            span.ClientIDMode = ClientIDMode.Static                             ' prevent ASP.NET element ID mangling
            e.Item.FindControl("lblTaskName").Parent.Controls.AddAt(0, span)    ' hook it into the repeater row

            Dim btnSignoff = CType(e.Item.FindControl("btnSignOff"), Button)
            Dim btnComment = CType(e.Item.FindControl("btnComment"), Button)
            Dim btnAmend = CType(e.Item.FindControl("btnAmend"), Button)
            Dim btnView = CType(e.Item.FindControl("btnView"), Button)
            Dim lblSoTime = CType(e.Item.FindControl("lblSOTime"), Label)
            Dim lblDueDate = CType(e.Item.FindControl("lblDueDate"), Label)
            Dim lblTaskId = CType(e.Item.FindControl("lblTaskId"), Label)
            Dim lblTaskName = CType(e.Item.FindControl("lblTaskName"), Label)

            lblTaskId.Text = CType(t.taskId, String)

            lblTaskName.Text = t.taskName

            Dim time = (If(t.taskDueTime Is Nothing, New TimeSpan(0, 23, 59, 59), TimeSpan.Parse(t.taskDueTime)))
            Dim dueDateTime As DateTime = (Occasion.started.Date + time)

            'Setup up due DateTime for Daily Tasks
            Select Case DirectCast(t.taskDayTypeId, helpers.Constants.enDayType)
                Case helpers.Constants.enDayType.Daily
                    lblDueDate.Text = dueDateTime.ToString("dd/MM/yyyy HH:mm")
                    Exit Select
                Case Else
                    'Calculate the actual due date for non-daily tasks
                    Dim calculator = New Calculator()
                    Dim calId = t.taskCalendarId
                    Dim taskMonthDay = "1"
                    If Not t.taskMonthDayId Is Nothing Then
                        taskMonthDay = CType(t.taskMonthDayId, String)
                    End If
                    Dim monthDay = _db.MonthDays.First(Function(m) m.id = CInt(taskMonthDay))
                    Dim calendar As Model.Calendar = Nothing
                    If Not calId is Nothing Then
                        calendar = _db.Calendars.First(Function(x) calId.Value = x.id)
                    End If
                    Dim potDate = calculator.GetActualDueDate(dueDateTime, monthDay.monthDay, t, calendar)
                    dueDateTime = (potDate.Date + time)
                    lblDueDate.Text = dueDateTime.ToString("dd/MM/yyyy HH:mm")
                    Exit Select
            End Select

Therefore once the data is displayed in the repeater the sorting is wrong. I need to be able to sort the data after the due date re-calculation. How is this possible?

Thanks

1
I dont understand the issue. The label-text is not related to the ordering of the datasource of the repeater. Why you want to order by the label-text at all? You're doing it right, you order by a DateTime not by a string. - Tim Schmelter
It won't solve your problem, but you may be able to improve performance (save some memory) by removing that ToList() call at the end of your data source. - Joel Coehoorn
Thanks for the comments guys. I've removed the ToList() as this was never needed. @TimSchmelter - basically my problem is I sort the datasource on page_load but I recalculate some of the data in ItemDataBound but by this time the data is already sorted. I want to sort by the re-calculated data - GaryBurge
@GaryBurge: why don't you "re-calculate" it before you assign the datasource? - Tim Schmelter

1 Answers

0
votes

You can to move the ItemDataBound logic up into the original query:

rptTask.DataSource = _db.vw_tasklists.
    Where(Function(o) o.occasionId = Occasion.id).
    Select(
          Function(r)
              'Fill in the logic here so the DueProperty has your real Due Date
              New With {.Row = r, .DueDate = r.TaskDueDate + r.TaskDueTime}
           End Function
    OrderBy(Function(t) t.DueDate). ' Now we only need one OrderBy (the time is already included).
    Select(Function(r) r.Row) 'But we do want to select back to the original record for the databinding
    'And the ToList() was probably NEVER needed or helpful in this situation

Moreover, since this looks like linq-to-sql I might break that up a bit, so we cleanly separate what we expect to execute on the database from what we expect to execute on the web server:

'This runs on the databsae
Dim sqlData = _db.vw_tasklists.
    Where(Function(o) o.occasionId = Occasion.id)

'This runs on the web server
rptTask.DataSource = sqlData.
    Select(
          Function(r)
              'Fill in the logic here so the DueProperty has your real Due Date
              New With {.Row = r, .DueDate = r.TaskDueDate + r.TaskDueTime}
           End Function
    OrderBy(Function(t) t.DueDate). ' Now we only need one OrderBy (the time is already included).
    Select(Function(r) r.Row) 'But we do want to select back to the original record for the databinding
    'And the ToList() was probably NEVER needed or helpful in this situation

Of course, you'll get the best results if you start putting information into the database such that you can effectively sort your data correctly there at the outset.