VBA Code
Sheets Collections - This is all the sheets including chart sheets
Worksheets Collections - This is all the sheets excluding chart sheets
Get the current worksheet.
let selectedSheet = workbook.getActiveWorksheet();
Get the worksheet with that name "Better".
let sheet = workbook.getWorksheet("Better");
if (sheet)
{
console.log("exists");
}
Switch to the new worksheet.
newSheet.activate();
Get all the worksheets in the workbook.
let sheets = workbook.getWorksheets();
Get a list of all the worksheet names.
let names = sheets.map ((sheet) => sheet.getName());
Get the total number of worksheets in a workbook.
console.log(`Total worksheets inside of this workbook: ${sheets.length}`);
Set the tab color each worksheet to a random color.
for (let sheet of sheets) {
let colorString = `#${Math.random().toString(16).substr(-6)}`;
sheet.setTabColor(colorString);
}
Delete a worksheet
sheet.delete();
Add a blank worksheet with the name "Solutions".
let newSheet = workbook.addWorksheet("Solutions");
Sheets Collection
Every workbook has a Sheets collection that contains both Worksheets and Chart sheets.
This is a collection of Object datatypes.
Although Excel provides a Sheets collection as a property of a Workbook object, there is no Sheet object.
Every member of the Sheets collection is either a worksheet or a chart sheet.
ActiveWorkbook.Sheets("Sheet1").
ActiveWorkbook.Sheets("Chart1").
ActiveWorkbook.Sheets(1).
It is necessary to declare a sheet as a generic Object type if you want to refer to Worksheets and Chart sheets, since there is no Sheet object.
The ActiveSheet property can return either a Chart object or a Worksheet object depending on what is currently active.
If TypeName(ActiveSheet) = "Chart Sheet" | "Worksheet"
Worksheets Collection
There is also a Worksheets collection that contains just the worksheets in a workbook (no chart sheets).
You can refer to a worksheet either by its name or by using its index number in the collection.
You should always try and use the name if possible to specify the exact member in the Worksheets collection.
ActiveWorkbook.Worksheets("Sheet1").
ActiveWorkbook.Worksheets(1).
A lot of the methods only work with the Worksheets collection and not the Sheets collection (even when you refer to the same worksheet).
The index number of a worksheet in the Worksheets collection can be different from the index number of the worksheet in the Sheets collection.
In the following workbook the first sheet is a Chart sheet.
Hiding Sheets
You can hide sheets to prevent users from displaying them
Sheets("Sheet2").Visible = xlSheetVisibility.xlSheetVeryHidden
Sheets("Sheet2").Visible = True | False
Using xlSheetVeryHidden will mean the worksheet is not displayed in the (Format > Sheet > Unhide) dialog box.
Avoid Using Worksheet Index
The Index property of the Worksheet object returns the value of the Index in the Sheets collection and not the Worksheets collection.
You should avoid using the Index property of a worksheet.
It is important to remember that the Index property of a worksheet refers to the position in the Sheets collection and not in the Worksheets collection.
If you run these two routines on a workbook that contains chart sheets this will be illustrated.
If you want to process all the worksheets in a workbook you should refer to each worksheet using its index number
Dim isheetno As Integer
For isheetno = 1 To ActiveWorksheet.Worksheets.Count
ActiveWorkbook.Worksheets(isheetno).Activate
Call Msgbox(Worksheets(isheetno).Name & " is active" & vbclrf & _
"This has index number " & Worksheets(isheetno).Index)
Next isheetno
Alternatively you could use the following:
Dim isheetno As Integer
For isheetno = 1 To ActiveWorksheet.Sheets.Count
ActiveWorkbook.Sheets(isheetno).Activate
Call Msgbox(Sheets(isheetno).Name & " is active" & vbclrf & _
"This has index number " & Sheets(isheetno).Index)
Next isheetno
Limiting the Scroll Area
Sheets("Sheet1").ScrollArea = "A1:E400"
Sheets("Sheet1").ScrollArea = ""
Worksheets("Sheet1").UsedRange
An alternative is to delete all the other rows and columns, hide row & col headers and then protect the worksheet
Sheets("Sheet1").Range("D4").Select
Sheet1.Range("A2").Select
icount = ActiveWorksheet.Sheets.Count - returns the total number of sheets (inc Chart sheets) in the active workbook
Remove all values on a worksheet just leaving the formulas
Cells.SpecialCells(xlCellTypeConstants, xlNumbers).ClearContents
Changing Tab Colour
oWsh.Tab.Color = RGB(20,20,20)
For Each Item In Windows
If Item.Visible = False Then
End If
Next Item
Exporting
You can export from a worksheet object or a workbook object
expression.ExportAsFixedFormat (Type = xlFixedFormatType.xlTypePDF _
FileName, Quality, IncludeDocProperties, IgnorePrintAreas, From, To, OpenAfterPublish, FixedFormatExtClassPtr)
Snippet - Identifying the Selected Sheets
Dim objworksheet As Worksheet
For Each objworksheet In ActiveWindow.SelectedSheets
Call MsgBox(objworksheet.Name)
Next objworksheet
Snippet - Worksheet Exists
Public Function Wsh_Exists(ByVal sWshName As String) As Boolean
Dim sName As String
On Error GoTo ErrorHandler
sName = ThisWorkbook.Sheets(sWshName).Name
If Len(sName) > 0 Then Wsh_Exists = True
Exit Function
ErrorHandler:
Wsh_Exists = False
End Function
Custom Properties
ActiveSheet.CustomProperties.Add Name:="MyName", Value:="My Value"
MsgBox ActiveSheet.CustomProperties.Item(1).Name
Prevent More Sheets
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Application.DisplayAlerts = False
Call MsgBox("You cannot add any more worksheets to this workbook", vbInformation + vbOKOnly)
Sh.Delete
Application.DisplayAlerts = True
End Sub
VBA - Worksheet Names
Tab/Display Name
You can refer to your worksheets in VBA code using there programmatic name.
This "name" property can be viewed from the Properties window in the VBE.
Sheet2.Range(A2").Select
CodeName
The following code always return ThisWorkbook
Debug.Print ActiveWorkbook.CodeName
This can only be used with ThisWorkbook
Debug.Print ActiveWorkbook.Worksheets(1).CodeName
Getting the Name of the Worksheet
One way of obtaining the name of the worksheet to appear in one of its cells is to use the Application.Caller property
This property returns a reference to the object that called (or executed) the procedure
When used in a user-defined worksheet function it returns a reference to the cell containing the formula
Public Function BET_GetWorkSheetName() As String
Application.Volatile(True)
Bet_GetWorksheetName = Application.Caller.Parent.Name
End Function
Getting the Name of the previous Worksheet
This worksheet function will always return the name of the previous worksheet.
The worksheet name could then be used in an INDIRECT function.
Public Function BET_PreviousSheet() As String
Dim isheetcount As Integer
Dim ssheetname As String
Application.Volatile(True)
ssheetname = Application.Caller.Parent.Name
BET_PreviousSheet = "N/A"
For isheetcount = 2 To ActiveWorkbook.Worksheets.Count
If ActiveWorkbook.Worksheets(isheetcount).Name = ssheetname Then
BET_PreviousSheet = ActiveWorkbook.Worksheets(isheetcount - 1).Name
End If
Next isheetcount
End Function
VBA - Worksheet Code Modules
Any code that has been place in a worksheet code module will default to referring to that specific worksheet.
The following will refer to the corresponding worksheet containing the code
Range()
Cells()
How to refer to the specific worksheet name when inside a worksheet code module
Dim objWorksheet As Worksheet
objWorksheet = Application.Worksheets(Range("A1").Parent.Name)
objWorksheet.Range("D4").Value = "some text"
General Code Modules
Any code placed in a General code module will default to the active worksheet.
The following two lines of code are equivalent when placed in a general code module.
Range()
ActiveSheet.Range()
VBA - Navigating Sheets
Manoeuvring
Selection.End(xlDown).Select
Selection.SpecialCells(xlCellTypeLastCell).Select
Switching between worksheets
Worksheets("Sheet1").Activate
Sheets("Sheet2").Select
Sheets(2).Select
Sheets(2).Activate
Scrolling / Position
ActiveWindow.ScrollColumn
This returns the column number in the top-left corner of the active window
ActiveWindow.ScrollRow
This returns the row number in the top-left corner of the active window
ScrollArea
This controls the range of cells where scrolling is allowed.
Cells & Ranges > Scroll Area
VBA - Activate / Select
Understanding the difference between Selecting and Activating
There is very little difference between activating a worksheet and selecting a worksheet.
The following means that worksheets "Sheet 1", "Sheet 2" and "Sheet 3" are all selected
Sheets(Array("Sheet1", "Sheet2", "Sheet3")).Select
When more than one worksheet is selected the first worksheet is always the active worksheet.
Activating sheets is a slow process.
You can only ever activate a single worksheet.
Sheets("Sheet2").Activate
Cannot activate different worksheets when several are selected
The first line will select three worksheets and by default the first worksheet will be the active worksheet.
Sheets(Array("Sheet1", "Sheet2", "Sheet3")).Select
The second line will not keep the current selection but will infact just select the "Sheet2" worksheet.
Sheets("Sheet2").Activate
This is different functionality to the Select and Activate when using a Range object.
ActiveSheet.Previous.Select ??
Selecting Sheets
Redim Preserve arNames(1 to 5)
Sheets(arNames).Select
Worksheets(2)
Worksheet Name - This is different to the worksheet code module name
SS - emm.com/excel-vba-worksheet
You can use the worksheet code module name in your code which can be useful if you think the user might rename the worksheet.
VBA - Inserting / Deleting
ActiveWorkbook
You can insert additional worksheets into the active workbook by adding to its sheets collection:
Application.ActiveWorkbook.Sheets.Add(Before, After, Count, Type)
Since ActiveWorkbook is the default member of the Application object you can also use:
Application.Sheets.Add(Before, After, Count, Type)
Since Application is the default member you could abbreviate it and use:
Sheets.Add(Before, After, Count, Type)
Inserting a Worksheet
To insert a worksheet after the worksheet that is currently selected you can use:
Sheets.Add(Type:=xlSheetType.xlWorksheet)
Because the sheet type xlWorksheet is the default you can shorten this to just:
Sheets.Add
If you are copying or moving worksheets around in a workbook you cannot debug/step through the code.
Whenever referencing workbooks or worksheets always enclose them in single speech marks "WshName" as they could contain spaces and/or unusual characters.
If your VBA code is generating Dr Watsons or Application Errors when trying to select a worksheet, then the workbook may be corrupt. Copy all the worksheets to a new workbook.
To quickly view the event procedure for a worksheet right - click the Sheet tab and click View Code.
If you give your worksheet names using the Property window you can refer directly to the name in code instead of using Sheets("---").
It is not possible to add a chart sheet after the last worksheet in a workbook. You will have to insert it elsewhere and then move it.
Dim oWsh As Excel.Worksheet
Set oWsh = Worksheets.Add
Add a Sheet First
Set oFirst = ThisWorkbook.Worksheets(1)
Set oNew = ThisWorkbook.Worksheets.Add(Before:= oFirst)
Add a Sheet Last
Set oLast = ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count)
Set oNew = ThisWorkbook.Worksheets.Add(After:= oLast)
List all worksheets
For icount = 1 To ThisWorkbook.Worksheets.Count
Debug.Print ThisWorkbook.Worksheets(i).Name
Next icount
Deleting Sheets
Public Function BET_WshDelete(sWshName As String)
Application.DisplayAlerts = bDisplayAlerts
Sheets(sWshName).Delete
Application.DisplayAlerts = True
End Sub
The last line is not actually needed as the DisplayAlerts is automatically reset, although it makes your code more readable
VBA - Moving Sheets
Moving an existing worksheet
The Copy and Move methods of the Worksheet object allow you to copy or move one or more worksheets in a single operation.
Both the methods have two optional parameters that allow you to specify the destination of the operation.
The destination can be either before or after a specified sheet.
If you do not use one of these parameters, the worksheet will be copied or moved to a new workbook.
Copy and Move do not return any value or reference.
The first sheet created by a Copy (or resulting from a Move) will be the immediatley active after the operation.
Worksheets(1).Move
VBA - Copying Sheets
Copying an existing worksheet
The Copy method of the Worksheet object allow you to copy one or more worksheets in a single operation.
The Copy method does not return any reference to the copies worksheet
There are two optional parameters that allow you to specify the destination of the operation.
The destination can be either before or after a specified sheet.
If you do not specify one of these parameters, then the worksheet will be copied to a new workbook.
The first sheet created by a Copy (or resulting from a Move) will be the immediatley active after the operation.
Worksheets(1).Copy After:=Worksheets(2)
Worksheets("Sheet1").Copy After:=Worksheets("Sheet3")
Worksheets("Template").Visible = True
Worksheets("Template").Copy After:=Sheets(Sheets.Count)
Worksheets("Template").Visible = xlsheetvisibility.xlsheetveryhidden
ActiveSheet.Name = "Name of new worksheet"
VBA - Grouping Sheets
Worksheets can be manually grouped by holding down the Shift (or Ctrl) key as you click on several sheets.
It is possible to group sheets by using the Select method of the Worksheets collection in conjunction with the Array function.
The following code groups the first, second and third sheets of a workbook and makes the second worksheet active.
Worksheets( Array(1,2,3) ).Select
Worksheets(2).Activate
It is also possible to group particular worksheets using the Select method of the worksheet object.
The first worksheet is selected in the normal way and subsequent worksheets are added to the group by using the Select method while setting its Replace parameter to False.
Worksheets("Sheet1").Select
Worksheets("Sheet1").Select Replace:=False
Worksheets("Sheet1").Select Replace:=False
This technique can be useful when you want to group specific sheets.
Making Changes
When you group sheets manually any changes or formatting changes made to one sheet are made to the all.
This is not the case when you apply changes to a group using VBA.
Only the active sheet is affected when you apply changes to a grouped sheet using VBA code.
The only way to make changes to all the worksheets in a group is to use a For-Each loop and apply the changes to each worksheet individually.
Set colSheets = Worksheets( Array(1,2,3) )
For Each objWorksheet in colSheets
Next objWorksheet
Currently Selected Sheets
If you want to identify the sheets that are currently grouped you can use the SelectedSheets property of the Window object.
For Each objWorksheet in ActiveWindow.SelectedSheets
Next objWorksheet
This property is a member of the Window object because you can open several windows of the same workbook and each window can display a different worksheet (or worksheet group).
Adding the following code will mean that any values enters into cells in the named range "NamedRange" on Sheet1 will automatically also appear in Sheet2 and Sheet3 automatically.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Range("NamedRange"), Target) Is Nothing Then
Sheets(Array("Sheet1", "Sheet2", "Sheet3")).Select
Else
Me.Select
End If
End Sub
If you want the same data to appear on other sheets but in a different place use the following:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Range("NamedRange"), Target) Is Nothing Then
Range("NamedRange").Copy Destination:=Sheets("Sheet2").Range("A1")
Range("NamedRange").Copy Destination:=Sheets("Sheet3").Range("C20")
Else
Me.Select
End If
End Sub
VBA - Protecting Worksheets
All the arguments are optional
ActiveSheet.Protect Password:="", _
DrawingObjects:=True, _
Contents:=True, _
Scenarios:=True
UserInterfaceOnly, _
AllowFormattingCells, _
AllowFormattingColumns, _
AllowFormattingRows, _
AllowInsertingColumns, _
AllowInsertingRows, _
AllowInsertingHyperlinks, _
AllowDeletingColumns, _
AllowDeletingRows, _
AllowSorting, _
AllowFiltering, _
AllowUsingPivotTables)
Password - A string that specifies a case-sensitive password for the worksheet or workbook. If this argument is omitted, you can unprotect the worksheet or workbook without using a password. Otherwise, you must specify the password to unprotect the worksheet or workbook. If you forget the password, you cannot unprotect the worksheet or workbook. It's a good idea to keep a list of your passwords and their corresponding document names in a safe place.
DrawingObjects - True to protect shapes. The default value is False.
Contents - True to protect contents. For a chart, this protects the entire chart. For a worksheet, this protects the locked cells. The default value is True.
Scenarios - True to protect scenarios. This argument is valid only for worksheets. The default value is True.
UserInterfaceOnly - True to protect the user interface, but not macros. If this argument is omitted, protection applies both to macros and to the user interface.
AllowFormattingCells - True allows the user to format any cell on a protected worksheet. The default value is False.
AllowFormattingColumns - True allows the user to format any column on a protected worksheet. The default value is False.
AllowFormattingRows - True allows the user to format any row on a protected. The default value is False.
AllowInsertingColumns - True allows the user to insert columns on the protected worksheet. The default value is False.
AllowInsertingRows - True allows the user to insert rows on the protected worksheet. The default value is False.
AllowInsertingHyperlinks - True allows the user to insert hyperlinks on the worksheet. The default value is False.
AllowDeletingColumns - True allows the user to delete columns on the protected worksheet, where every cell in the column to be deleted is unlocked. The default value is False.
AllowDeletingRows - True allows the user to delete rows on the protected worksheet, where every cell in the row to be deleted is unlocked. The default value is False.
AllowSorting - True allows the user to sort on the protected worksheet. Every cell in the sort range must be unlocked or unprotected. The default value is False.
AllowFiltering - True allows the user to set filters on the protected worksheet. Users can change filter criteria but can not enable or disable an auto filter. Users can set filters on an existing auto filter. The default value is False.
AllowUsingPivotTables - True allows the user to use pivot table reports on the protected worksheet. The default value is False.
With the exception of the AllowEditRanges property, the Protection object's properties are set when you use the Protect method to protect a worksheet.
If you apply the Protect method with the UserInterfaceOnly argument set to True to a worksheet and then save the workbook, the entire worksheet (not just the interface) will be fully protected when you reopen the workbook. To re-enable the user interface protection after the workbook is opened, you must again apply the Protect method with UserInterfaceOnly set to True.
If changes wanted to be made to a protected worksheet, it is possible to use the Protect method on a protected worksheet if the password is supplied. Also, another method would be to unprotect the worksheet, make the necessary changes, and then protect the worksheet again.
Note 'Unprotected' means the cell may be locked (Format Cells dialog) but is included in a range defined in the Allow Users to Edit Ranges dialog, and the user has unprotected the range with a password or been validated via NT permissions.
Accessing Properties
ActiveSheet.Protection.AllowDeletingColumns = False
ActiveSheet.Protection.AllowDeletingRows = False
ActiveSheet.Protection.AllowFormattingCells = False
ActiveSheet.Protection.AllowFormattingRows = False
ActiveSheet.Protection.AllowFormattingColumns = False
ActiveSheet.Protection.AllowInsertingRows = False
ActiveSheet.Protection.AllowInsertingColumns = False
ActiveSheet.Protection.AllowInsertingHyperlinks = False
ActiveSheet.Protection.AllowFiltering = False
ActiveSheet.Protection.AllowSorting = False
ActiveSheet.Protection.AllowEditRanges = False
ActiveSheet.Protection.AllowUsingPivotTables = False
Protecting Worksheets - Features Disabled
Even though this option is automatically disabled for a worksheet that is protected the following line of code does work.
1) (Edit > Delete)
2) (Edit > Links)
3) (Edit > Object)
4) (Format > Cells)(Alignment tab)("Merge cells")
Selection.MergeCells = True
5) (Insert > Name)
6) (Insert > Picture)
The From File command allows you insert a graphic from a file.
This line of code will not work when the worksheet is protected
ActiveSheet.Pictures.Insert("C:\Temp\Picture.bmp")
The Autoshapes command just displays the Drawing toolbar and the AutoShapes toolbar and these can be displaying manually using (View > Toolbars > Drawing)
VBA - Synchronizing Sheets
When you move from one worksheet to another the sheet you activate will be configured as it was when it was last active.
The top-left hand corner cell, the selected range of cells and the active cell will be in exactly the same position as they were the last time that worksheet was active (unless you are in Group mode).
If you are in Group mode, the selected range and the active cell are synchronized across all the worksheets
However the top-left hand corner cell is not synchronized.
(please refer to snippet in scrap book for a way to synchronise properly).
When you switch between worksheets the previous selection and range is preserved
The top left cell, current selection and the active cell are all preserved (ie in the position you left them)
If you are in Group Mode then this is not the case
If you are in Group Mode then the top left cell, current selection and active cell are synchronized across the group.
Public gSheet As Object
Private Sub Workbook_SheetDeactivate(ByVal Sht As Object)
If TypeName(Sht) = "Worksheet" Then Set gSheet = Sht
End Sub
Private Sub Workbook_SheetActivate(ByVal NewSheet As Object)
Dim lcol As Long
Dim lrow As Long
Dim sCell As String
Dim sSelection As String
If gSheet Is Nothing Then Exit Sub
If TypeName(NewSheet) <> "Worksheet" Then Exit Sub
If (NewSheet Is Excel.Worksheet) Then Exit Sub
gSheet.Activate
lcol = ActiveWindow.ScrollColumn
lrow = ActiveWindow.ScrollRow
sSelection = Selection.Address
sCell = ActiveCell.Address
NewSheet.Activate
ActiveWindow.ScrollColumn = lcol
ActiveWindow.ScrollRow = lrow
Range(sSelection).Select
Range(sCell).Activate
End Sub
VBA - Find and Replace
This method is only applicable to individual worksheets.
If you want to replace in the whole workbook you need to loop through all the worksheets.
The macro recorder will record the same thing regardless of whether you select sheet or workbook
Find Method
Finds information in particular range
Returns a range that represents the first cell where the information is found.
Returns Nothing if no matches can be found.
This is searching the active worksheet
Cells.Find What:="test to find", _
Replacement:="replace it with this", _
LookIn:=xlValues, _
LookAt:=xlLookAt.xlPart, _
SearchOrder:=xlSearchOrder.xlByRows, _
SearchDirection:=xlSearchDirection.xlNext, _
MatchCase:=False, _
MatchByte:=False, _
SearchFormat:=False
What - The text you want to search for.
Replacement / After - A single cell to start the search from. The default is the top left cell of the range.
LookIn - This settings is saved each time you use this method. Allowing you to call the method again without specifying the value.
LookAt - This settings is saved each time you use this method. Allowing you to call the method again without specifying the value.
SearchOrder - This settings is saved each time you use this method. Allowing you to call the method again without specifying the value.
SearchDirection - This settings is saved each time you use this method. Allowing you to call the method again without specifying the value.
MatchCase - The default is False. Allows you to make the search case sensitive
MatchByte - The default is False. Allows you to only match double-byte characters.
SearchFormat - Allows the search to respect the FindFormat options. These options can be changed from Application.FindFormat.
FindNext
Can be used to repeat the search.
When the search completes it loops back to the start again
To avoid a continous loop you must compare the first cell with the cell returned.
FindPrevious
Can be used to repeat the search.
Replace Method
Dim objRange As Range
Set objRange = Range("A1:D20")
objRange.Replace What:="test to find", _
Replacement:="replace it with this", _
LookAt:=xlLookAt.xlPart, _
SearchOrder:=xlSearchOrder.xlByRows, _
SearchDirection:=xlSearchDirection.xlNext, _
MatchCase:=False
What - The text you want to search for.
Replacement - The text you want to replace it with.
LookAt - This setting is saved each time you use this method. Allowing you to call the method again without specifying the value.
SearchOrder - This settings is saved each time you use this method. Allowing you to call the method again without specifying the value.
SearchDirection - This settings is saved each time you use this method. Allowing you to call the method again without specifying the value.
MatchCase - The default is False. Allows you to make the search case sensitive
MatchByte - The default is False. Allows you to only match double-byte characters.
SearchFormat - Allows the search to respect the FindFormat options. These options can be changed from Application.FindFormat.
Dim objRange As Range
objFind = objRange.Find("some text", MatchCase:=False)
objFind.Address
objRange.FindNext(objFind)
Find and Replace Equal Signs
This is the equivalent of updating all the formulas
Cells.Replace What:="="
Replacement:="="
LookAt:=xlLookAt.xlWhole
You can use the following commands to reset these dialog boxes back to their default values
Application.FindFormat.Clear
Application.ReplaceFormat.Clear
Replacing @
Sub Testing()
Dim wsh As Excel.Worksheet
Dim rgeSelection As Excel.Range
Set wsh = ActiveSheet
Set rgeSelection = Application.Selection
rgeSelection.Replace What:="@", _
Replacement:="", _
LookAt:=XlLookAt.xlPart, _
SearchOrder:=XlSearchOrder.xlByRows, _
MatchCase:=False, _
MatchByte:=False, _
SearchFormat:=False, _
ReplaceFormat:=False, _
FormulaVersion:=xlReplaceFormula2
End Sub
© 2026 Better Solutions Limited. All Rights Reserved. © 2026 Better Solutions Limited TopPrev