Search code examples
excelvbauserform

Repeat Active Filter After Userform Entry


I have a macro that sorts clients based off of "Contact Dates", "Deposits", and "Credit". Say I select to sort by "Contact Date" and then I add a new client to my list, how would can I have the active sort re-run after I have entered a new client from my user form?

Userform I use to add client data

UserForm I use to enter Data

Filter Options

Filter Options

Here is my code:

Credit Balance Sort

Sub creditbalance()

    Dim w As Long, lr As Long, wss As Variant

    wss = Array("contactunder")


    For w = LBound(wss) To UBound(wss)
        With ThisWorkbook.Worksheets(wss(w))
            lr = Application.Max(.Cells(.Rows.Count, "a").End(xlUp).Row, _
                                 .Cells(.Rows.Count, "da").End(xlUp).Row)
            With .Range(.Cells(10, "a"), .Cells(lr, "da"))
                .Cells.Sort Key1:=.Columns(97), Order1:=xlDescending, _
                            Orientation:=xlTopToBottom, Header:=xlYes
            End With
        End With
    Next w

End Sub

Contact Date Sort

Sub contactdate()
    Dim w As Long, lr As Long, wss As Variant

    wss = Array("contactunder")


    For w = LBound(wss) To UBound(wss)
        With ThisWorkbook.Worksheets(wss(w))
            lr = Application.Max(.Cells(.Rows.Count, "a").End(xlUp).Row, _
                                 .Cells(.Rows.Count, "da").End(xlUp).Row)
            With .Range(.Cells(10, "a"), .Cells(lr, "da"))
                .Cells.Sort Key1:=.Columns(2), Order1:=xlDescending, _
                            Orientation:=xlTopToBottom, Header:=xlYes
            End With
        End With
    Next w

End Sub

Deposit Balance Sort

Sub depositbalance()
    Dim w As Long, lr As Long, wss As Variant

    wss = Array("contactunder")


    For w = LBound(wss) To UBound(wss)
        With ThisWorkbook.Worksheets(wss(w))
            lr = Application.Max(.Cells(.Rows.Count, "a").End(xlUp).Row, _
                                 .Cells(.Rows.Count, "da").End(xlUp).Row)
            With .Range(.Cells(10, "a"), .Cells(lr, "da"))
                .Cells.Sort Key1:=.Columns(68), Order1:=xlDescending, _
                            Orientation:=xlTopToBottom, Header:=xlYes
            End With
        End With
    Next w
End Sub

Solution

  • The little code you've shown is very redundant - by making the hard-coded Key1 sort argument a parameter, you instantly remove the need for two of these three clones, and repurpose the third to do the job for all three.

    Sorting and applying a sort, is something very easy to do when your range is a ListObject aka "table". Take your range, select "format as table" from the Home Ribbon. Now you never need to work out the last row ever again.

    Also, if the wss(w) sheet exists in ThisWorkbook at compile-time, there's no reason to dereference it from the Worksheets collection - just use its code name identifier (you can change it by selecting the sheet in the Project Explorer / Ctrl+R, then changing its (Name) property in the Properties toolwindow / F4) - then you can do TheSheetName.Range("whatever"). Or better - since that code only ever needs to work on a specific sheet, put it in that sheet's code-behind, and use Me to refer to the Worksheet instance:

    Public Sub ApplySortOrder(Optional ByVal sortColumn As String = vbNullString)
    
        With Me.ListObjects(1)
    
            Dim sortColumnRange As Range
            If sortColumn <> vbNullString Then
                'assumes sortColumn is an existing column header
                Set sortColumnRange = .ListColumns(sortColumn).DataBodyRange
            End If
            With .Sort
                If Not sortColumnRange Is Nothing Then
                    .SortFields.Clear
                    .SortFields.Add sortColumnRange
                End If
                .Apply
            End With
        End With
    
    End Sub
    

    Now, assuming I got the assumed column headings right, the code you have that's calling depositbalance can look like this:

    TheSheetName.ApplySortOrder "DepositBalance"
    

    Sorting by contactdate would be this:

    TheSheetName.ApplySortOrder "ContactDate"
    

    Sorting by creditbalance:

    TheSheetName.ApplySortOrder "CreditBalance"
    

    And if you want to re-apply the current sorting:

    TheSheetName.ApplySortOrder
    

    And the day you need to sort by something else, you can just do:

    TheSheetName.ApplySortOrder "ThatFancyNewColumn"
    

    And be done with it, without needing to copy-paste yet another procedure.

    You could even declare a Public Enum for the valid columns...

    Public Enum SortingColumn
        Current = 0
        CreditBalance = 97
        DepositBalance = 68
        ContactDate = 2
    End Enum
    

    Then change the signature to accept a SortingColumn parameter:

    Public Sub ApplySortOrder(Optional ByVal sortColumn As SortingColumn = Current)
    
        With Me.ListObjects(1)
    
            Dim sortColumnRange As Range
            If sortColumn <> Current Then
                'assumes sortColumn is an existing column header
                Set sortColumnRange = .ListColumns(sortColumn).DataBodyRange
            End If
            With .Sort
                If Not sortColumnRange Is Nothing Then
                    .SortFields.Clear
                    .SortFields.Add sortColumnRange
                End If
                .Apply
            End With
        End With
    
    End Sub
    

    Or even better, leave out the explicit enum values, and map each value to a string column name - then write a function that gets you the ListColumn.Index for it, so the user can't rename the headings, but they can still move these 90-something columns as they please. ...but that's for another post I guess.