Existing code
Main sort routine
Code:
Sub ReorgBy3Criteria()
Rem 0 error handling
On Error GoTo TheEnd:
Rem 1 worksheet info
Call DieseArbeitsmappe1.FillMeGlobsUpMate ' Global variables filled for example that for open daily Diet Protokol
Dim objwinToSort As Object: Set objwinToSort = Windows("" & DieseArbeitsmappe1.ProWb.Name & "") 'Mainly for convenience, but would give possibility of referring to a sheet "quasi" as the active even if you do not have got it "active" in front of you
Dim wksToSort As Worksheet: Set wksToSort = DieseArbeitsmappe1.ProWb.Worksheets("" & "Sheet1" & "")
Rem 2 Range for sort is based on Window selection. For convenience only rows selection is necerssary
Dim StRow As Long, stClm As Long, StpRow As Long, StpClm As Long
Let StRow = objwinToSort.Selection.Row: Let stClm = 1 'objwinToSort.Selection.Column ' Select any column or columns of rows to sort
Let StpRow = StRow + objwinToSort.Selection.Rows.Count - 1: Let StpClm = 3488 ' 3482 '454 '99
Dim rngToSort As Range: Set rngToSort = wksToSort.Range(CL(stClm) & StRow & ":" & CL(StpClm) & StpRow) ' Use column letter function for column letters
Dim ArrrngOrig() As Variant: Let ArrrngOrig() = rngToSort.Value ' This is used as a back up to restore the original range
Let Application.EnableEvents = False ' This is necerssary to turn off some event coding which I have which springs into action when anything is done in the worksheet
Rem 3 VBA Range.Sort Method
' xlDescending Biggest at Top H Kcal J Fett L eiweiss
'rngToSort.Sort Key1:=wksToSort.Columns("h"), order1:=xlDescending, Key2:=wksToSort.Columns("j"), order2:=xlDescending, Key3:=wksToSort.Columns("l"), order3:=xlDescending 'X Nat
'Standard unter ---- Kcal Highest H ,at Top , second most J Fett , highest at Top , third Natrium X , most at top
rngToSort.Sort Key1:=wksToSort.Columns("H"), order1:=xlDescending, Key2:=wksToSort.Columns("J"), order2:=xlDescending, Key3:=wksToSort.Columns("X"), order3:=xlDescending 'X Nat
Let Application.EnableEvents = True
Rem 4 Msg Box check if all is well, if not put original enties back in. COMMENTED OUT FOR SPEED TESTS
Dim Response As Integer 'In VBA Butons "yes is 6, 7 is "no"
Let Response = MsgBox(Prompt:="Is all OK?", Buttons:=vbYesNo, Title:="File Check") ' Displays a message box with the yes and no options.
If Response = vbYes Then 'Do nothing, that is to say just carry on after End of If
' all is well - carry on after End If
Else
Let Application.EnableEvents = False
Let rngToSort.Value2 = ArrrngOrig() 'Full repair!!Put back as it was
Let Application.EnableEvents = True
End If
Exit Sub ' Routine end if no errors____________________________________________________________________
TheEnd: ' Error handling code section
Let Application.EnableEvents = True ' In the Case of an error I want to ensure that I turn back on my normal events. This is necerssary incase the error occured between after a .EnableEvents = False and before a .EnableEvents = True
MsgBox Prompt:=Err.Number & vbCr & vbLf & Err.Description
End Sub 'ReorgBy3Criteria
Coding Walkthrough Explanation
Rem 1 is not particularly relevant to the Sort process. This is just getting at the data worksheet containing the spreadsheet range to be sorted.
In this actual example a global variable, is held in the ThisWorkbook code module, ProWb, holding the data workbook as an object. This would usually be filled on opening of the File containing the sort routine. Just in case it may not be filled, a routine is called to fill it. ( For English Excel, the routine would be placed in the ThisWorkbook code module, and referencing this variable would be by ThisWorkbook. FillMeGlobsUpMate in place of the German equivalent in my pre pre alpha Excel 2019 version which has the objects module DieseArbeitsmappe1
Rem 2 concerns determination of the range to be sorted, which in this case is the selection made in the active Window. In the actual example there are many columns and the number is known. The rows can be variable. ( Typical row selections are made in a large table and just those rows are sorted based on a few of the column values )
The Selection is used to obtain the start ( Top ) row, from an available property
objwinToSort.Selection.Row
and the stop ( bottom ) row is , as is often the case in VBA things, not available as a property, but the rows count is , so the typical formula is used to get that of pseudo like Stop = Start + ( RowCount – 1 )
StRow + objwinToSort.Selection.Rows.Count - 1
The column numbers of the range are hardcoded
I prefer to obtain my Range from the Range property method like Range(“A23:ZZ89”), so in order to do this when I use the column numbers, I use a simple function, CL( )
An array is made , ArrrngOrig() , to use as a back up from which to restore the original unsorted range if required later
Rem 3 main sort
A typical example is used , showing the Range.Sort method to sort the range by 3 columns. As main sort this will be having Kcal values of the highest at the top. For similar Kcal values the sort will be by the Fat , again with the larges at the top. In case any rows have the same Kcal and Fat values, then a last sort for those rows is made by highest Natrium at the top. ( Natrium = Sodium , which is a Salt derivative )
Code:
'Standard unter ---- Kcal Highest H ,at Top , second most J Fett , highest at Top , third Natrium X , most at top
rngToSort.Sort Key1:=wksToSort.Columns("H"), order1:=xlDescending, Key2:=wksToSort.Columns("J"), order2:=xlDescending, Key3:=wksToSort.Columns("X"), order3:=xlDescending
Rem 4 Possible reset of original range order
I would tend to usually include such a coding section as this in any sorting routine. The user has the possibility to accept the sorted range or use the array, ArrrngOrig(), to restore the original range unsorted.
Required Function and routine
In normal code module
Code:
Function CL(ByVal lclm As Long) As String 'Using chr function and Do while loop For example http://www.excelforum.com/excel-programming-vba-macros/796472-how-to-go-from-column-number-to-column-letter.html
'Dim rest As Long 'Variable for what is "left over" after subtracting as many full 26's as possible
Do
' Let rest = ((lclm - 1) Mod 26) 'Gives 0 to 25 for Column Number "Left over" 1 to 26. Better than ( lclm Mod 26 ) which gives 1 to 25 for clm 1 to 25 then 0 for 26
' Let FukOutChrWithDoWhile = Chr(65 + rest) & FukOutChrWithDoWhile 'Convert rest to Chr Number, initially with full number so the "units" (0-25), then number of 26's left over (if the number was so big to give any amount of 26's in it, then number of 26's in the 26's left over (if the number was so big to give any amount of 26 x 26's in it, Enit ?
' 'OR
Let CL = Chr(65 + (((lclm - 1) Mod 26))) & CL
Let lclm = (lclm - (1)) \ 26 'This gives the number of 26's ( if any ), but just the excact part, in the next number down , - so applying the rest formula to this new number will again leave a difference "left over" rest.
'lclm = (lclm - (rest + 1)) \ 26 ' As the number is effectively truncated here, any number from 1 to (rest +1) will do in the formula
Loop While lclm > 0 'Only loop further if number was big enough to still have 0-25's in it
End Function
' https://www.excelforum.com/development-testing-forum/1101544-thread-post-appendix-no-reply-needed-please-do-not-delete-thanks-4.html#post4213887
Required routine in ThisWorkbook code module
In this actual example a global variable, is held in the ThisWorkbook code module, ProWb, holding the data workbook as an object. This would usually be filled on opening of the File containing the sort routine. Just in case it may not be filled, a routine is called to fill it. ( For English Excel, the routine would be placed in the ThisWorkbook code module, and referencing this variable would be by ThisWorkbook. FillMeGlobsUpMate in place of the German equivalent in my pre pre alpha Excel 2019 version which has the objects module DieseArbeitsmappe1
Code:
'' DieseArbeitsmappe1
' In Lis Workbook code module
Option Explicit
Public ProWb As Workbook '
Public Sub FillMeGlobsUpMate()
For Each ProWb In Workbooks
If VBA.Left$(ProWb.Name, 18) = "ProAktuellex8600x2" Then
'Set ProWb = ProWb ' Don't need this - it is effectively left set at current Workbook in loop, because you exit for.
Set LisWbProWb = ProWb
Exit For ' This is important or else you will need to uncommment Set ProWb = ProWb
Else
' MsgBox Prompt:="You don't seem to have a Pro open, my luv, x" The error handling, PoGoStRory: will do this for me onn the attempt at ProWb.Name
End If
Next ProWb
End Sub
Data Workbook ( for sample range ):
"ProAktuellex8600x2.xlsx" https://app.box.com/s/sspcm7a4w3t99wzezruxep7e7j51khju
Bookmarks