3

I've been struggling with this issue in the past 3 days, so please help...

What I want to do is to when I run a macro1 (for the sake of the argument):

  1. Window would pop up to select a range of which cells should be sorted
  2. Have these sorted via last column selected (or the 5th) (lowest to highest numbers)

The issue here is that selected area would change eveytime (I create something like a tree in excel), so it cannot be a specific column that needs to be sorted by the last one (or the 5th in this case) of the selected (in the code below I do not know how to change I11:I15)

What I got and it does not work:

Sub RangeSelectionPrompt()
    Dim rngStart As Range
    Set rngStart = Application.InputBox("Select a range", "Obtain Range Object", Type:=8)

    Set rngStart = Selection

    rngStart.Select
    ActiveWorkbook.Worksheets("CALCULATION").Sort.SortFields.Clear
    ActiveWorkbook.Worksheets("CALCULATION").Sort.SortFields.Add Key:=Range( _
        "I11:I15"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal
    With ActiveWorkbook.Worksheets("CALCULATION").Sort
        .SetRange rngStart
        .Header = xlGuess
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
End Sub
Zoe
  • 27,060
  • 21
  • 118
  • 148
John Jones
  • 39
  • 1
  • 2
  • extract the end column from rngStart and use that to set the Key for the sort. You will also need to handle the user not selecting anything or canceling. – QHarr Jan 23 '18 at 09:41
  • I wouldn't recommend nesting in this way but you can get column with Dim endCol As String : endCol = Split(Split(rngStart.Address, ":")(1), "$")(1) and then concatenate with the header row to create the key e.g. Range(endCol & 1) – QHarr Jan 23 '18 at 09:49
  • Your `rngStart` variable is all mucked up at the start. You start by obtaining the range by selecting it, when you press OK the selected range is saved in the variable and the actual selected cell returns to whatever was selected before you ran the code. The next line then tells it to save the selected cell in the variable - so it saves whatever was selected before the code started. The next line then tells it to select whatever's in the variable - which will always be the current selection, so that line doesn't do anything. The provided answers appear to sort that problem though. :) – Darren Bartrup-Cook Jan 23 '18 at 11:37

3 Answers3

1

You can get the end column of rngStart as a Range with:

rngStart.Columns(rngStart.Columns.Count)

Using a With to tidy this up, you could do the following:

With rngStart
    ActiveWorkbook.Worksheets("CALCULATION").Sort.SortFields.Add Key:= _
        .Columns(.Columns.Count), SortOn:=xlSortOnValues, Order:= _
        xlAscending, DataOption:=xlSortNormal
End With

You could also tidy up the ActiveWorkbook.Worksheets by instead taking the Parent of rngStart.

Lastly, you want to trap the error that would occur if the user clicks Cancel instead of selecting a range. There are a number of ways of doing this but the first one that came to mind was using an On Error.. trap.

Here's the whole code:

Sub RangeSelectionPrompt()

    Dim rngStart As Range
    Dim WS As Worksheet

    On Error Resume Next
    Set rngStart = Application.InputBox("Select a range", "Obtain Range Object", Type:=8)
    Err.Clear
    On Error GoTo 0

    If rngStart Is Nothing Then
        MsgBox "User cancelled"
    Else
        Set WS = rngStart.Parent
        WS.Sort.SortFields.Clear

        With rngStart
            WS.Sort.SortFields.Add Key:= _
                .Columns(.Columns.Count), SortOn:=xlSortOnValues, Order:= _
                xlAscending, DataOption:=xlSortNormal
        End With

        With WS.Sort
            .SetRange rngStart
            .Header = xlGuess
            .MatchCase = False
            .Orientation = xlTopToBottom
            .SortMethod = xlPinYin
            .Apply
        End With
    End If
End Sub
CLR
  • 11,284
  • 1
  • 11
  • 29
0

Try to get the range on which you sort (I11:I15) as a separate variable. In order to do this, you need the last column of your intital range and the last row of it.

In the code below, the range you sort is rngSort and it is defined through

Set rngSort = .Parent.Range(.Parent.Cells(firstRow, lastCol), _
                            .Parent.Cells(lastRow, lastCol))

To get the last column and the last row, you need:

lastCol = .Cells(.Count).Column
lastRow = .Rows(.Rows.Count).Row

Once you are ready with the rngSort then you simply change the I11:I15 part in your code with it:

Option Explicit

Sub RangeSelectionPrompt()

    Dim rngStart    As Range
    Dim rngSort     As Range

    Dim lastCol     As Long
    Dim lastRow     As Long
    Dim firstRow    As Long
    Dim firstCol    As Long 'you do not need it

    Set rngStart = Application.InputBox("Select a range", "Obtain Range Object", Type:=8)
    With rngStart
        lastCol = .Cells(.Count).Column
        lastRow = .Rows(.Rows.Count).Row
        firstCol = .Cells(1, 1).Column
        firstRow = .Cells(1, 1).Row
        Set rngSort = .Parent.Range(.Parent.Cells(firstRow, lastCol), _
                                    .Parent.Cells(lastRow, lastCol))
    End With

    ActiveWorkbook.Worksheets("CALCULATION").Sort.SortFields.Clear
    ActiveWorkbook.Worksheets("CALCULATION").Sort.SortFields.Add Key:=rngSort, _
    SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal
    With ActiveWorkbook.Worksheets("CALCULATION").Sort
        .SetRange rngStart
        .Header = xlGuess
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
End Sub
Vityata
  • 42,633
  • 8
  • 55
  • 100
0

Go back a step from the recorded VBA sort to what is actually required and use transpose to change your inputbox range to a one dimensioned array.

Dim vCustom_Sort As Variant, rr As Long, rng As Range

Set rng = Application.InputBox("Select a range", "Obtain Range Object", Default:=Selection.Address, Type:=8)

vCustom_Sort = Application.Transpose(rng)
Application.AddCustomList ListArray:=vCustom_Sort

With Worksheets("Sheet4")    '<~~ set this properly!
    .Sort.SortFields.Clear
    rr = .Cells(.Rows.count, "A").End(xlUp).Row
    With .Range("A1:A" & rr)
        .Cells.Sort Key1:=.Columns(1), Order1:=xlAscending, _
                    Orientation:=xlTopToBottom, Header:=xlYes, MatchCase:=False, _
                    OrderCustom:=Application.CustomListCount + 1

    End With
    .Sort.SortFields.Clear
End With

p.s. If you are going to execute a VBA Sort command, you should know whether you have a header row or not.

Before sub procedure with local E2:E9 selected.

enter image description here

After sub has executed.

enter image description here

  • The OP's range is more than one column, but he wants to sort by the last column of the selected range. – Vityata Jan 23 '18 at 10:15
  • yes, this is boilerplate code that should be easily adaptable to the OP's specific needs. There is some confusion between rngStart and Key:=Range("I11:I15"). My purpose was to show how to select a range to be used as a custom sort criteria; perhaps I got it wrong. –  Jan 23 '18 at 10:25
  • In my defence, sample data together with expected results may have helped clarify the situation. –  Jan 23 '18 at 10:28
  • I am not attacking you. :) Indeed, sample + a screenshot usually gets what is needed really fast. Or best case scenario - "input - output" in the codeforces.com or topcoder.com format. :) – Vityata Jan 23 '18 at 10:30
  • Oh... if it was only that simple. ** –  Jan 23 '18 at 10:33