VBA .font.bold optimization - vba

I am formatting two different reports using the same macro. The performance between the two spreadsheets is dramatically different. The macro functions instantaneously on the first report and takes about 10 seconds on the other. I have narrowed it down to the statement which makes cell A1 bold. When the bolding statement is omitted, both spreadsheets execute the macro instantaneously. I have tried the following methods of formatting cell A1.
Range("A1").Font.Bold = True
Range("A1:A1").Font.Bold = True
With Range("A1")
.Font.Bold = True
End With
ActiveSheet.Cells(1, 1).Font.Bold = True
As far as I know, .Font.Bold = True is the only way to make a cell bold, so the problem cannot be circumvented by using another command. The above are all the ways Google has suggested calling such action. Both spreadsheets are .xls extensions. Why might one spreadsheet's performance be dramatically different when executing the same statement?

There may be a lot of reasons. The main in my opinion being that the changes require ScreenUpdating and both Worksheets use a different amount of memory to compute your Worksheet layout e.g. having shapes, pivots, charts, hidden rows etc.
To workaround this turn off ScreenUpdating before your computations. Then turn it on again like below
Application.ScreenUpdating = False
'Your code here
Application.ScreenUpdating = True
Additionally to optimize your code utilize the following:
Turn off AutomaticCalculations
Save your file as binary Excel: .xslb

Related

Excel VBA: efficient way to load data in a workbook with a lot of formulas

I have an Excel VBA code that reads the master data, load the data of each case/loop into a workbook, and then tons of calculations take place via numerous formulas (with no VBA codes). The desired result is the outputs of the calculations. The code loops through all the cases in the master data.
So here is the problem. I used multiple 'copy and paste' actions in each loop to load the data into the workbook, but the run time is way longer than I expected, causing Excel to be 'Not Responding'. Note that I already turned off automatic calculations in Excel and added the workbook Calculate trigger in the VBA code, so as to avoid updating the workbook entirely every time there is a paste action.
Could someone advise if directly setting the cells in the wb = the cells in the master data would speed up or slow down the code? Or could someone suggest a more efficient way of loading data?
I appreciate your effort before you send in your responses.
As said in the comments, not using .Select improves the speed of your macro. Also, copy paste actions are indeed more slow whith VBA and you should do assignations FromWorksheet.Range("CopyRange").Value = ToWorksheet.Range("PasteRange").Value. Also, it always helps to add these lines in the beginning:
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
Application.DisplayAlerts = False
and reactivating them at the end:
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.DisplayStatusBar = True
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
There are many information on this, like this link

VBA Calculations Turning Back On Mid Execution

I'm running a large macro that includes various sub queries and functions.
Despite having the standard code to turn off calculations while the macro is running, somehow calculations are turned back on during the code execution.
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
I've added the above code the beginning of each major sub but still have the same issue. There's one point in the macro where I calculate the application to update a worksheet stored value, but I can't imagine that's what's causing it.
application.Calculate
Trying to find if there's a work around.

Slow performance setting Excel sheet print settings

Using the Microsoft Office Interop libraries for Excel, I've written a routine in VB.Net that creates a large number of Excel worksheets, populates them and then formats them for printing. For every worksheet I call this small method to set the print settings:
Public Sub SetDefaultReportPrintSettings(orientation As ReportSheetOrientation, ws As Excel.Worksheet)
CType(ws.Parent, Excel.Workbook).Application.Windows(1).Zoom = 90
With ws.PageSetup
Select Case orientation
Case ReportSheetOrientation.Portrait : .Orientation = Excel.XlPageOrientation.xlPortrait
Case ReportSheetOrientation.Landscape : .Orientation = Excel.XlPageOrientation.xlLandscape
End Select
.Zoom = False
.FitToPagesTall = 1
.FitToPagesWide = 1
.LeftMargin = ws.Application.InchesToPoints(If(orientation = ReportSheetOrientation.Portrait, 0.75, 0.5))
.RightMargin = ws.Application.InchesToPoints(0.5)
.TopMargin = ws.Application.InchesToPoints(0.75)
.BottomMargin = ws.Application.InchesToPoints(0.75)
.CenterHorizontally = True
End With
End Sub
When I benchmark, this sub alone is apparently taking about 0.98 seconds. I don't understand why toggling a handful of settings would take that long. It might not seem like much, but when generating hundreds (or more) sheets this really adds to the routine time. The Excel application instance is hidden, so I don't think this is a screen updating issue.
Any way to make this go faster?
Chris Neilen's comment (set Application.PrintCommunication=False during PageSetup modifications) significantly improved my run time. According to documentation, Excel by default is actively communicating with the printer, which helps explain the slow performance.
I got an even bigger speed gain by setting Excel's view to Normal instead of Page Break Preview. This made a big difference, not in the PageSetup code, but for the rest of my Excel routines. The documentation again explains Excel does additional calculations for page breaks.
https://msdn.microsoft.com/en-us/library/office/ff835544.aspx
https://support.microsoft.com/en-us/kb/199505

Reordering this recorded macro code to not use Select?

I've got this code in Excel using the macro recorder, which unticks the 'Locked' property of a slicer:
ActiveSheet.Shapes.Range(Array("WeekEndingPick")).Select
Selection.Locked = msoFalse
I don't like using Select \ Selection in VBA (except when I specifically want to visibly select a range) for a number of reasons*, it seems poor practice. Usually I can just rewrite things like this easily:
'Macro-recorded code
Range("A1").Select
Selection.Font.Bold = True
'Equivilant without select / selection:
Range("A1").Font.Bold = True
However, the following doesn't work as Locked is not a property of Range, it's a property of Shape:
ActiveSheet.Shapes.Range(Array("WeekEndingPick")).Locked = msoFalse
How can I work around this?
*If the required worksheet isn't active, it fails. If Application.ScreenUpdating = False, it fails. If the selection changes unexpectedly, all manner of nonsense can occur - not necessary failing, but running code against the wrong location.
Figured it almost as soon as I went back to fiddle around in Excel. It's a property of Slicer:
ThisWorkbook.SlicerCaches("Slicer_WeekEndingPick").Slicers("WeekEndingPick").Locked = msoFalse
Simple!

What Excel VBA actions are possible on hidden worksheets or workbooks?

Hidden worksheets/workbooks have some limitations to what can be done in VBA code, like most Select and Selection statements, and anything coming from ActiveSheet, but I can't seem to find any list of what the limitations are.
Google, the built-in documentation in the help system, and MSDN's website have all failed me. Can anyone point me in the right direction?
Edit:
The workbook is opened with
Set WB_Master = Workbooks.Open(Filename:=PATH_Master, ReadOnly:=False)
and then hidden with
WB_Master.Windows(1).Visible = False
From the Visual Basic for Applications help:
When an object is hidden, it's removed from the screen and its Visible property is set to False. A hidden object's controls aren't accessible to the user, but they are available programmatically to the running application, to other processes that may be communicating with the application through Automation, and in Windows, to Timer control events.
Not much help there I'm afraid, and I couldn't find much else through Google.
As you said yourself, the Select method and Selection Property don't work on a hidden Worksheet, they should work on a hidden Workbook though. (Please correct me if I'm wrong.) In general however, it's not always all that efficient to select ranges in worksheets anyway, you are better off working with the Range property (which does work on a hidden worksheet).
EDIT:
The following code will change the color of A1:A8 to Cyan even when the Worksheet is not visible:
Dim book2 As Workbook
Set book2 = Workbooks.Open("C:\Book2.xls")
book2.Worksheets("Sheet1").Visible = False
book2.Windows(1).Visible = False
With book2.Worksheets("Sheet1").Range("A1:E8").Interior
.ColorIndex = 8
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
End With
book2.Windows(1).Visible = True
book2.Worksheets("Sheet1").Visible = True
You can get around any limitations on hidden sheets by unhiding them without the user realizing it, doing whatever you need to, and then hiding them again.
This example assumes that Sheet2 is hidden.
Sub DoStuffToAHiddenSheetWithoutTheUserKnowingIt()
'turns off screen repainting so the user can't see what you're doing
'incidentally, this dramatically speeds up processing of your code
Application.ScreenUpdating = False
'note that if you're stepping through your code, screenupdating will be true anyway
'unhide the sheet you want to work with
Sheets("sheet2").Visible = True
'do whatever you want here, including selecting cells if you want
'Scagnelli is right though, only select cells if you have to
'when you're finished, hide the sheet again
Sheets("sheet2").Visible = False
'make sure you turn screenupdating back on, or Excel will be useless
Application.ScreenUpdating = True
End Sub
Another useful trick if you want your sheets hidden is to set them to xlVeryHidden, which will prevent them from being listed to the user if they try to unhide them through the menu or ribbon.