views:

379

answers:

3

I'm working on a sortable listview with data-paging. I've got the listview and paging working fine, but I'm struggling with with the sorting. The sorting in itself is fine; I can sort by a particular column (ASC or DESC), however I'm having problems when it comes to dynamic sorting where the user can pick a column and, at times, reverse the sort direction.

My main problem is that currently the generation of sorted and paged data is triggered both in the Form_Load event handler and in the Listview_Sorting event handler. Ideally, I'd want the population of the listview to be handled in one way for form (re)loading and when selecting a new page of data, and in another way when the user clicks on the column header (ie when (re)sorting). Unfortunately, when the sorting event is fired, the code in Form_Load is executed and then later the code in ListView_Sorting is executed.

Initially it was merely an inefficiency that I was prepared to let slide, but now some of the Form_Load code is fouling what I'm doing in the Sorting event handler.

So my question is... how do I seperate the handling of these events into two groups; how can I run one set of code when the page loads for the first time and when data is paged from when I'm trying to sort the data?

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

 'Initilize the Sort Column and Direction
 Dim LastColumn As String = If(Session("SortColumn") Is Nothing, "LastWriteTime", Session("SortColumn"))
 Dim SortDirection As SqlClient.SortOrder
 Dim SortDirections As Dictionary(Of String, SqlClient.SortOrder) = Session("SortDirections")

 If SortDirections Is Nothing OrElse Not SortDirections.ContainsKey(LastColumn) Then
  SortDirection = SqlClient.SortOrder.Descending
 Else
  SortDirection = SortDirections(LastColumn)
 End If

 Call GenerateSortedArray(LastColumn, SortDirection)

End Sub

Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

 'bind array to ListView
 Me.lvwMSGs.DataBind()

End Sub

Private Sub lvwMSGs_ItemCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewCommandEventArgs) Handles lvwMSGs.ItemCommand

 Dim file As FileInfo = New FileInfo(e.CommandArgument.ToString) '-- if the file exists on the server

 If e.CommandName = "Stream" Then
  If file.Exists Then 'set appropriate headers
   Response.Clear()
   Response.AddHeader("Content-Disposition", "attachment; filename=" & file.Name)
   Response.AddHeader("Content-Length", file.Length.ToString())
   Response.ContentType = "application/octet-stream"
   Response.WriteFile(file.FullName)
   Response.End()
  Else 'if file does not exist
   Response.Write("This file does not exist.")
  End If
 End If
End Sub

Public Sub lvwMSGs_Sorting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewSortEventArgs) Handles lvwMSGs.Sorting

 Call GenerateSortedArray(e.SortExpression, SqlClient.SortOrder.Ascending)

End Sub

Private Sub GenerateSortedArray(ByVal SortColumn As String, ByVal DefaultSortDirection As SqlClient.SortOrder)
 Dim dirInfo As New DirectoryInfo(Server.MapPath(AppSettings.Item("ContentDir")))
 Dim FileArrayList As New ArrayList(dirInfo.GetFiles("*.msg", SearchOption.TopDirectoryOnly))

 Dim SortDirections As New Dictionary(Of String, SqlClient.SortOrder)

 With FileArrayList
  .TrimToSize()

  SortDirections = Session("SortDirections")
  If Session("SortDirections") Is Nothing OrElse SortDirections.ContainsKey(SortColumn) Then
   'Create dictionary, set to default and store in Session variable
   If Session("SortDirections") Is Nothing Then
    SortDirections = New Dictionary(Of String, SqlClient.SortOrder)
   End If
   SortDirections(SortColumn) = DefaultSortDirection
   Session("SortDirections") = SortDirections

   'Sort data according to preferences
   .Sort(New FileInfoComparer(SortDirections(SortColumn), SortColumn))
  Else
   'retrieve previous sort direction
   SortDirections(SortColumn) = 1 - SortDirections(SortColumn)

   'Sort data according to preferences
   .Sort(New FileInfoComparer(SortDirections(SortColumn), SortColumn))

  End If

 End With

 With Me.lvwMSGs
  .DataSource = FileArrayList
  .ItemPlaceholderID = "ItemPlaceholder"
 End With
End Sub
A: 

You can check for IsPostback in Page_Load. The first time loaded, IsPostback is false, and on any postbacks it is true.

If it is a problem with the order of the page lifecycle, you can do stuff that is dependent on the result from postbacks in the Page_PreRender event, which is triggered after all control postback events.

EDIT:
What if you move the call to GenerateSortedArray(LastColumn, SortDirection) from Page_Load to Page_PreRender ?

Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

    'Initilize the Sort Column and Direction
    Dim LastColumn As String = If(Session("SortColumn") Is Nothing, "LastWriteTime", Session("SortColumn"))
    Dim SortDirection As SqlClient.SortOrder
    Dim SortDirections As Dictionary(Of String, SqlClient.SortOrder) = Session("SortDirections")

    If SortDirections Is Nothing OrElse Not SortDirections.ContainsKey(LastColumn) Then
            SortDirection = SqlClient.SortOrder.Descending
    Else
            SortDirection = SortDirections(LastColumn)
    End If
    GenerateSortedArray(LastColumn, SortDirection)

    'bind array to ListView
    Me.lvwMSGs.DataBind()

End Sub
awe
IsPostback doesnt help (on it's own at least) because data-paging uses postbacks.
CJM
No the order isn't a problem (as yet!) - I can uses IsPostBack to distinguish between the first load and all other scenarios, but I can't distinguish between paging and sorting.
CJM
+1  A: 

As suggested by awe, it should suffice to place the sorting code in page load in anif (!Page.IsPostBack) { // default page load sorting code here } block.

At a functional level however, you might want to create a generic function that accepts the Sort Column and Direction as parameters and does the sorting for you. You should then simply have to call this function from page load using the default sort column/direction (!Page.IsPostback condition still applies), and from the ListView_Sorting event based on the event arguments.

You could possibly structure you code in the following manner :

Handle Page_Load
{
    if (!Page.IsPostBack)
    {
        // First Time Load Only
        // Identify DEFAULT Sort + DEFAULT Paging values
        // BuildGrid(Default Sort params, Default Paging params);
    }

    // there should be no data grid code in page load on a postback
}

Handle Sorting_Event
{
    // Determine NEW sorting values
    // Fetch Existing Paging values
    // BuildGrid (NEW Sort Params, OLD Paging Params);
}

Handle Paging_Event
{
    // Determine NEW paging values
    // Fetch Existing Sorting values
    // BuildGrid (OLD Sort Params, NEW Paging Params);
}
Preets
IsPostback doesnt help (on it's own at least) because data-paging uses postbacks.
CJM
As you can see above, I have a GeneratSortedArray sub that accepts such arguments. The problem is the logic which dictates which values are fed into the sub - I need to distinguish between paging and sorting.
CJM
Can you show me the code for paging ? Where are you handling that ?
Preets
A: 

CJM, In order for sorting and paging to work for you, you should do the following:

  • Page_Load : Load the FileArrayList into an member variable, if !Page.IsPostBack, sort
  • OnPreRender : Bind FileArrayList to ListView
  • GenerateSortedArray : just handle sorting the array (not binding)
  • Paging Event: Set the paging

This would require moving some of your code around. The way it is now, whatever paging event you add won't be able to properly access your list.

Also, I'd change your Session variables to ViewState, unless you expect a user to have only one page open at a time.

Jim Schubert