Been trying for days to figure out how to do a two-level auto sort in VBA. I've managed to get a single level auto-sort working, but when I try adding a second level it overrides the first level sorting I did previously. Data is kept in Rows.
Here is what I am trying to set up to autosort
This is the VBA i have written right now for the 1st level or sorting which Sorts the Active? Column
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Not Intersect(Target, Range("A:A")) Is Nothing Then
Range("A1").Sort key1:=Range("A2"), _
Order1:=xlDescending, Header:=xlYes, _
OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom
End If
End Sub
Here's the new formula I wrote thanks to your comments. Seems to work. My problem before was I kept doing Key2 and Order2 as a new If function instead of as part of the previous one.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:A")) Is Nothing Then
Range("A1").Sort key1:=Range("A2"), _
Order1:=xlDescending, Header:=xlYes, _
OrderCustom:=1, MatchCase:=False, _
key2:=Range("B2"), _
Order2:=xlAscending, Header:=xlYes, _
Orientation:=xlTopToBottom
End If
End Sub
Related
Code is as follows:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Not Intersect(Target, Range("A:A")) Is Nothing Then
Range("H2").Sort Key1:=Range("A2"), _
Order1:=xlAscending, Header:=xlYes, _
OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom
End If
End Sub
Is it possible to increase the scope of the code so that no matter where a date is entered in column A (below the A2 starting parameter) the date and row will be sorted into the correct location? Currently this code only allows the space directly after the final entry to sort.
Example:
Date
Other Info
5/12/2022
""Data
5/18/2022
''Data
5/17/2022
''Data
This produces a chart where the 5/17 will move between the 5/12 and 5/18 as it should
Example2:
Date
Other Info
5/12/2022
""Data
5/18/2022
''Data
--------
--------------
5/17/2022
''Data
This however results in nothing occurring which is what I want to increase the scope for. Is that possible?
Thanks
You could detect the last used row (examining column A) each time the event runs, with something like:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet, LastRow As Long
Set ws = ActiveSheet
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
On Error Resume Next
If Not Intersect(Target, Range("A:A")) Is Nothing Then
Range("A2:H" & LastRow).Sort Key1:=Range("A2"), _
Order1:=xlAscending, Header:=xlYes, _
OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom
End If
End Sub
However, an easier way would be to just use the Target.Row, as this should suffice in your circumstances:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Not Intersect(Target, Range("A:A")) Is Nothing Then
Range("A2:H" & Target.Row).Sort Key1:=Range("A2"), _
Order1:=xlAscending, Header:=xlYes, _
OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom
End If
End Sub
Obviously, if your table goes beyond column H, then you'll need to change that.
I have the following code that dos what I want except for being applied to a specific range. In other words, it does sort on multiple columns, and it does auto update when data is changed:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Not Intersect(Target, Range("D3:F")) Is Nothing Then
Columns("A:Z").Sort Key1:=Range("F3"), Key2:=Range("E3"), Key3:=Range("D3"), _
Order1:=xlAscending, Order2:=xlAscending, Order3:=xlAscending, Header:=xlYes, _
OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom
End If
End Sub
However, what it does is that it is applied to the whole range of "A:Z", whereas I want it to be applied to a specific range only (say, "A3:Z").
I have very limited knowledge of Excel VBAs, and did what I thought was the solution, changing Columns("A:Z").Sort to Columns("A3:Z").Sort, and Range("A3:Z").Sort, but the code stops working after this change.
Any help would be greatly appreciated!
Please, try replacing of
Columns("A3:Z").Sort
with
Range("A3:Z" & Range("A" & Rows.count).End(xlUp).Row).Sort
This will set the range to be sorted like starting from A3 to last cell in column Z:Z, but based on the last cell in column A:A.
Edited:
Your event code should look like this:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("D3:F" & Rows.Count)) Is Nothing Then
Range("B2:Z" & Range("A" & Rows.Count).End(xlUp).Row + 1).Sort Key1:=Range("F2"), Key2:=Range("E2"), Key3:=Range("D2"), _
Order1:=xlAscending, Order2:=xlAscending, Order3:=xlAscending, Header:=xlYes, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom
End If
End Sub
Please, copy the above code instead of yours and test it. When you set the range for the second row, of course, the Keys should be adapted to this row, too. And your code should neve reach the sorting part using Range("D3:F").
Besides all that, you never should use On error resume next until you have a working code and need after that to create a error handler. Otherwise, it only not let you seeing the code real problems/errors...
Edited:
You can also use the next approach (using SortFields):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("D3:F" & Rows.Count)) Is Nothing Then
With ActiveSheet
.Sort.SortFields.Clear
.Sort.SortFields.Add2 Key:=Range("F3:F" & Rows.Count) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Sort.SortFields.Add2 Key:=Range("E3:E" & Rows.Count) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Sort.SortFields.Add2 Key:=Range("D3:D" & Rows.Count) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With .Sort
.SetRange Range("B2:Z" & Range("B" & Rows.Count).End(xlUp).Row + 1)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End With
End If
End Sub
I think it is obvious that you should choose between one of them. If both of them on the sheet code module, an error will be raised...
In case someone really naïve like me had the same problem, here's what works:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Not Intersect(Target, Range("D3:F")) Is Nothing Then
Range("A3", Range("Z3").End(xlDown)).Sort Key1:=Range("F3"), Key2:=Range("E3"), Key3:=Range("D3"), _
Order1:=xlAscending, Order2:=xlAscending, Order3:=xlAscending, Header:=xlNo, _
OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom
End If
End Sub
Applying the formula to all the rows below A3
I am trying to find out how to get a database to automatically sort alphabetically using VBA in column A. Sounds easy, but I have headers in the first 4 rows and want it to sort from line 5 downwards. I have been searching for days to find a code that does this. The nearest I have succeeded is with this code-
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Not Intersect(Target, Range("A:A")) Is Nothing Then
Range("A1").Sort Key1:=Range("A2"), _
Order1:=xlAscending, Header:=xlYes, _
OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom
End If
End Sub
The problem is when I try changing the line
Range("A1").Sort Key1:=Range("A2"), _ to Range("A5").Sort Key1:=Range("A6"), _ when I test it, it still sorts to row 2 and not to row 5 as intended. I know I am missing something, but just cannot see what it is that I am missing!
Please do not misuse OERN (On Error Resume Next). It is like telling the code to Shut up :). Handle the error correctly.
Another interesting read
Is this what you are trying?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lRow As Long
On Error GoTo Whoa
'~> Find the last row in Col A
lRow = Range("A" & Rows.Count).End(xlUp).Row
'~~> Check if it is greater than row 4
If lRow > 4 Then
Application.EnableEvents = False
'~~> Check if the change happened in the relevant range
If Not Intersect(Target, Range("A5:A" & lRow)) Is Nothing Then
'~~> Sort only the relevant range
Range("A4:A" & lRow).Sort Key1:=Range("A4"), _
Order1:=xlAscending, _
Header:=xlYes, _
OrderCustom:=1, _
MatchCase:=False, _
Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
End If
End If
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
I am new to VBA and having a hard time figuring out this code.I am doing a course online and i followed the same steps but somehow i'm getting an error.I recently installed Excel 2013 and dont know if thats the issue.I tried putting .Range("A4") but that gives me an error of "Invalid or Unqualified Reference"
Sub DivisionSort()
'
' Sort List by Division Ascending
'
'
Selection.Sort Key1:=Range("A4"), Order1:=xlAscending, Header:=xlGuess, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
End Sub
Sub CategorySort()
'
' Sort List by Category Ascending
'
'
Selection.Sort Key1:=Range("B4"), Order1:=xlAscending, Header:=xlGuess, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
End Sub
Sub TotalSort()
'
' Sort List by Total Sales Ascending
'
'
Selection.Sort Key1:=Range("F4"), Order1:=xlAscending, Header:=xlGuess, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
End Sub
Public Sub SortList()
Dim userinput As String
userinput = InputBox("1=Sort by Division, 2=Sort by Category,3=Sort by Total")
If userinput = "1" Then
DivisionSort
ElseIf userinput = "2" Then
CategorySort
ElseIf userinput = "3" Then
TotalSort
End If
End Sub
Try to avoid use of 'Selection'. Try instead a full reference:
ThisWorkbook.Sheets("Sheet1").Range("Table1").Sort
Why does this work:
Range(Cells(1, 1), Cells(5, 5)).Select
Selection.Sort Key1:=Range("A1"), Order1:=xlAscending, Header:=xlNo, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
But this doesn't?:
Range(Cells(1, 1), Cells(5, 5)).Select
Selection.Sort Key1:=Range(Cells(1,1)), Order1:=xlAscending, Header:=xlNo, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
Saying the
Method Range failed
EDIT
The reason for asking is that I would like the sort key to be dynamic, such as:
Selection.Sort Key1:=Range(Cells(intRow, intCol))
I can't see how this is done.
The Cells call is already returning a Range object, so you should use
Selection.Sort Key1:=Cells(1,1), Order1:=xlAscending, Header:=xlNo, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
I think that your confusion is stemming from the fact that passing two Cells parameters to Range is valid, i.e. Range(Cells(1, 1), Cells(5, 5)), but it is not valid to only pass one Cells parameter, i.e. Range(Cells(1, 1))
You can see this for yourself with the following snippet
Public Sub test()
Dim rng As Range
Set rng = Range(Cells(1, 1), Cells(3, 1))
MsgBox rng.Cells.Count
Set rng = Range(Cells(1, 1))
MsgBox rng.Cells.Count
End Sub
You will get a message saying 3 for the first msgbox call, but you get an exception when trying to set rng the second time.
As to why the second format is not valid, I have no idea. If you find out why the devs built it this way, let us know.
It's always best to qualify exactly which objects you are working with and work directly with the objects, as opposed to using Selection or just Range, as it can sometimes lead to unintended consequences or slow your code down.
Try this:
Dim ws as Worksheet
Set ws = Sheets("Sheet1") ' replace with your sheet name
'assume you wrap this around a loop to move through intRow and intCol _
or set them in some other fasion
With ws.Range(Cells(1,1), Cells(5,5)
.Sort Key1:=.Cells(intRow, intCol), Order1:=xlAscending, Header:=xlNo, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
End With
The reason for asking is that I would like the sort key to be dynamic ...
At least part of the problem is relying on .Select and the subsequent Selection as the working area. If your intention is to work with the Range.CurrentRegion property (the 'island' of data origination in A1) then use a With ... End With statement to define the .CurrentRegion and work with it.
with worksheets("Sheet1") `<~~ set the parent worksheet!
with .Cells(1, 1).CURRRENTREGION `<~~ set the 'island' of data
.cells.Sort Key1:=.cells(1), Order1:=xlAscending, Header:=xlNo, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
end with
end with
I'm a little unclear on what you mean by 'dynamic'. The above will use the cell in the top-left of the area defined by .CurrentRegion. If you used With .Range("D5:H99") then .Cells(1) would refer to D5.
See How to avoid using Select in Excel VBA macros for more methods on getting away from relying on select and activate to accomplish your goals.