Category Archives: Advanced

Advanced level information

Speeding up VBA with the PrintCommunication Property in Excel 2010

I have an Excel file that creates different reports from a data table using a macro, and before Excel 2010 was released the “time-hog” was running VBA code for the print settings. Tell me you’ve added something like the following code and had your execution time slow exponentially.
With ActiveSheet.PageSetup
.CenterHeader = "&""Verdana,Bold""&12&A"
.CenterFooter = "&P of &N"
.LeftMargin = Application.InchesToPoints(0.7)
.RightMargin = Application.InchesToPoints(0.7)
.TopMargin = Application.InchesToPoints(0.75)
.BottomMargin = Application.InchesToPoints(0.75)
.HeaderMargin = Application.InchesToPoints(0.3)
.FooterMargin = Application.InchesToPoints(0.3)
.CenterHorizontally = True
.CenterVertically = False
.Orientation = xlPortrait
.FitToPagesWide = 1
.FitToPagesTall = False
End With

Another factor is the default printer of your computer when the code runs. I usually set my default printer to CutePDF, which isn’t really a printer, but a program that writes (prints) to a PDF file. Using this default printer while running the PageSetup code chops mega-time off the execution.

One trick I used was to run this code in the Workbook_BeforePrint module. That way, running my macro to create a new report doesn’t execute the PageSetup code and therefore executes much, much, much, much, much faster.

The New PrintCommunication Property

In Excel 2010 I happen to record a macro for print settings and discovered a new page setup property:

Application.PrintCommunication

The Application.PrintCommunication property is new in Excel 2010. It specifies whether communication with the printer is turned on. The values are Boolean and Read/Write.

Using the PrintCommunication Property

Set the Application.PrintCommunication equal to FALSE, then run the Page Setup code, then set it back to TRUE and your code will run exponentially faster. However, there are two problems with using the Application.PrintCommunication property and both deal with compatibility.

Compatibility with Older Versions

Older versions of Excel don’t recognize this new property so you have to code around this fact to ensure backward compatibility. I checked the Excel 2010 version number by running some code in the VBA Editor’s Immediate window.

Excel Version code

You can also check your Excel version by choosing File > Help and reading the screen.

Since the Application.version property returns a String value, the Val function is required to make use of it. My code now reads like this to achieve backward compatibility:
If Val(Application.Version) >= 14 Then Application.PrintCommunication = False
With ActiveSheet.PageSetup
' (all the ugly page setup code goes here)
End With
If Val(Application.Version) >= 14 Then Application.PrintCommunication = False

I checked to see how long it takes to run the VBA code to produce my report in three versions of Excel: 2010, 2007 and 2003.

Excel 2010 – run1 = 0.918 sec, run2 = 0.867 sec
Excel 2007 – run1 = 5.965 sec, run2 = 6.273 sec
Excel 2003 – run1 = 6.066 sec, run2 = 6.125 sec

My default printer at the time was a HP D110 and it took over 5 seconds longer to run the PageSetup code in Excel 2007 and 2003.

Compatibility with Excel 2011 for Mac

By now I’m happy with Excel for adding this new property, but reality sets in and I realize that Excel 2011 can run code too. I check to see what version number Excel 2011 for Mac happens to be, and it too is version 14.0.

Excel 2011 version

So while I’m in the immediate window I check for the Application.PrintCommunication property and find out it doesn’t exist.

No Print Communications property

So I have to use the OperatingSystem property of the Application object to give me another condition.

Excel 2010 Operating System

Excel 2010 Operating System

Excel 2011 Operating System

Excel 2011 Operating System

I use the Left function to grab the first three letters of the operating system name.

Left(Application.OperatingSystem, 3) = “Win”

Then change my code to account for earlier Excel versions AND the Windows operating system as follows:

If Val(Application.Version) >= 14 And Left(Application.OperatingSystem, 3) = "Win" _
Then Application.PrintCommunication = False
' (my page setup code here)
If Val(Application.Version) >= 14 And Left(Application.OperatingSystem, 3) = "Win" _
Then Application.PrintCommunication = True

Using the PrintCommunication property of the Application Object will help speed up your code execution. However, you need to be aware of backward compatibility for earlier versions of Excel that don’t recognize the code, and with the Mac version of Excel 2011 that should have the same property, but doesn’t.

Automatically Expand a Named Range in Excel

I usually put a name to each data table created for referencing information in in Excel, as in a Named Range. If you add data to the bottom of the table, the Named Range isn’t modified and any reference to it will fail to include the new information.

Here’s a table of data I stuck on a worksheet called MyData and the range A2:E10 is named myFoodData.

Named Range to Expand

Inserting a row inside this range will automatically expand the reference for the Named Range, but normally a user would add data to bottom of the table in the first empty row.

My solution is event based. I write a simple subroutine.

Sub ShiftRangeAndRename()
Const n As String = "myFoodData"
Dim rng As Range
Set rng = Range(n).CurrentRegion
Set rng = rng.Offset(1, 0).Resize(rng.Rows.Count - 1, rng.Columns.Count)
rng.Name = n
End Sub

Then reference it from the deactivate routine on the MyData worksheet.

Private Sub Worksheet_Deactivate()
Call ShiftRangeAndRename
End Sub

When a user goes to the MyData worksheet and updates data and returns to the main worksheet, the worksheet deactivate routine calls the routine to update the range reference and its associated name. This also works if they are deleting data, but that’s not common in this type of situation.

Calculate the Xth Weekday of Any Month in Excel

I’ve a simple formula for calculating the Xth Weekday of ANY month. It takes four inputs: Year, Month, Week, and Day. And requires a couple of lookup tables for data validation, one with special formatting.

Xth Weekday of a Month

The History

It all started when my wife mentioned there were a couple of meetings she had to schedule at work, one on the second Tuesday of every month and the other on the third Tuesday of every month. That got me to thinking about how to create a formula in Excel to calculate those dates.

After getting a partial solution, other things took precedence and it slipped from my mind until Chandoo posted a homework article on how to figure out what date Thanksgiving falls on each year, the 4th Thursday of November, in the USA.

His solution and all the variations provided in the comments (I posted one too) was focused and specific to one day (Thursday) of one week (the 4th) in one month (November). Albeit a link in that article showed another formula solution to Canada’s Thanksgiving day that takes place on the 2nd Monday of October.

This served to spur me on and finish what I started. I hope you like it.

The Inputs

Xth Weekday of the Month

Input for the Year in cell A2 is done manually. Just type in a year.

Input for Month in cell B2 is from a data validation drop-down list of values from 1-12. Merely done for convenience.

Input for Week in cell C2 is from a data validation drop-down list of values from 1-4. These are the only values that work correctly in the formula.

Input for Day in cell D2 is from a data validation drop-down list of values from 1-7 that use the “ddd” custom cell format to convert those numbers into an actual date. (You can’t actually type in a text value because what you’re seeing is a formatted date. Below I’ll show an alternate formula where you can use text values like Sun, Mon, … Sat.)

The Formula

The formula for Date Selected in cell E2 is uses the “ddd, m/d/yyyy” custom date format (US) and the formula is:

=DATE(A2,B2,(C2*7+1)-WEEKDAY(DATE(A2,B2,8-DAY(D2))))

Instead of putting all the calculations in the Day argument of the DATE function, you can also write the formula as DATE – WEEKDAY:

=DATE(A2,B2,(C2*7+1))-WEEKDAY(DATE(A2,B2,8-DAY(D2)))

The Explanation – Why This Works

WEEKDAY Inverse RelationshipThis formula has two parts, subtracting the WEEKDAY from the DATE. They both hinge on the fact that WEEKDAY function values are 1-7 for days Sun-Sat, and that an inverse relationship exists for the first week of the month, as shown by the chart. (Eight minus the WEEKDAY value)

Column three is the Day value, or 3rd argument, of the DATE function inside the WEEKDAY function. This relationship works for every day of the week. For a Tuesday, we have WEEKDAY(DATE(Year, Month, 5)). For a Thursday we have WEEKDAY(DATE(Year, Month, 3)).

The DATE Function relies on the Week number 1-4 to generate a date that is one day past that particular week. C2 * 7 +1, is just giving values 8, 15, 22, or 29.

So for a Thursday Thanksgiving in the USA you have

DATE(Year, 11, 29 – WEEKDAY(DATE(Year, 11, 3)))

and for a Tuesday Thanksgiving in Canada you have

DATE(Year, 10, 15 – WEEKDAY(DATE(Year, 10, 6)))

It helps to visualize this formula in the Evaluate Formula dialog box and step through the formula iteration.

Evaluate Formula Xth Weekday of a Month

The Data Validation

One critical aspect of this formula is to get a numerical date value from cell D2 for the DAY function. I use values 1-7 and convert them to Dates by custom formatting the cells with the “ddd” format.

Format Integers to Day Format

Of course this resulted in my date range being 1 Jan, 1900 to 7 Jan, 1900 and is okay for the Windows Date System.

Note: Excel for Mac users check to make sure you’re not using the 1904 Date System, in Excel>Preferences>Calculation look under the Workbook options to see if Use the 1904 date system is unchecked.

If you want to make this work with either date system (Windows or Mac) then choose a month where Sunday is the first day, like 1 Aug, 2010. You’ll need seven dates ending with 7 Aug, 2010.

Alternate Formula Without Custom Formatting

You can, of course, skip the custom formatting for the Day in cell D2 by using straight text values in the data validation list.

Data Validation Text Date List

This requires a different formula and below I’m utilizing the VLOOKUP function.

=DATE(A2,B2,(C2*7+1)-WEEKDAY(DATE(A2,B2,VLOOKUP(D2,{“Sun”,7;”Mon”,6;”Tue”,5;”Wed”,4;”Thu”,3;”Fri”,2;”Sat”,1},2,FALSE))))

The VLOOKUP reads cell D2 then finds the corresponding value in the constant array and returns a number.

Evaluate Xth Weekday of a Month Alternate

To shorten this up considerably, create a Named Constant, like MyWeekday, to replace the array.

Named Constant Array for MyWeekday

The formula can then be reduced to:

=DATE(A2,B2,(C2*7+1)-WEEKDAY(DATE(A2,B2,VLOOKUP(D2,MyWeekday,2,FALSE))))

I’m sure there are other variations. Do you have a better solution?

Download the Files

Here’s a file with the original formula that you can download.

XLSX File Format: Excel 2007, 2010, 2011

XLS File Format: Excel 2003, 2008

Here’s another file with the alternate formula using the VLOOKUP with constant array.

XLSX File Format: Excel 2007, 2010, 2011

XLS File Format: Excel 2003, 2008

Single-Cell Array Formula in Excel

A Single-cell Array formula is powerful, yet can be hard to understand. The Evaluate Formula dialog box helps by revealing array values Excel holds in internal memory.

In the spreadsheet you see pictured, a single-cell array formula for the Cumulative Plan is located in cell B2. It summarizes the historical and current day’s Plan data. The Cumulative Actual formula, in cell C2, summarizes the Actual data in the same manner.

As an introduction to Array formulas, in my last post I added two columns. The multi-cell array formulas for columns D and E are:

{=$A$2>=A5:A18}

{=D5:D18*B5:B18}

Extra Columns with multi-cell array formulas

Both formulas are entered by selecting their respective ranges, typing in the formula, without curly braces, and using Ctrl+Shift+Enter. Excel provides the curly braces, confirming it’s an array formula.

I didn’t include the current day in my last post, but decided to make a slight change in the column D formula here. (>= instead of >)

In the column D formula, the absolute reference to cell $A$2 could be replaced with the TODAY() function with the same result.

To summarize these two formulas: The Past compares Today’s date to the Date in column A and returns FALSE if the Date is in the future, and TRUE if not.

The MTD Plan multiplies this result times the Plan, and because TRUE = 1 and FALSE = 0, all rows with future date values become zero (0) and otherwise shows the value for the Plan.

A Temporary Single-Cell Array Formula

Summarizing the MTD Plan in a single cell with an array formula will give me a temporary solution for the Cum Plan. In cell B2 I enter the array formula:

{=SUM((D5:D18)*(B5:B18))}

Again, this is done by entering =SUM((D5:D18)*(B5:B18)) and pressing Ctrl+Shift+Enter. Excel provides curly braces.

This formula multiplies together two arrays, then summarizes with the SUM function.

With a single-cell array formula, the Evaluate Formula dialog box allows me to see how this formula is being process by Excel. On the Ribbon, select the Formulas tab, then click Evaluate Formula. In Excel 2003 it’s Tools → Formula Auditing → Evaluate Formula. Excel for Mac doesn’t have this feature.

Evaluate Formula Single cell array step 1

To see the result of the underlined expression, click the Evaluate button. You can see below that D5:D18 is an array that Excel is holding in internal memory. The curly braces are a dead give-away for an array.

Evaluate Formula Single cell array step 2

The next time the Evaluate button is clicked, B5:B18 shows up as a second array with numerical values.

Evaluate Formula Single cell array step 3

Recall from my last post that TRUE = 1 and FALSE = 0. Multiplying these two arrays together gives a single array, which you can see by clicking the Evaluate button again.

Evaluate Formula Single cell array step 4

The SUM function will now evaluate the array. Clicking Evaluate one more time will show the resulting answer, which is 2000.

My Single-Cell Array Formula

I now want to get rid of columns D and E so all references to them have to be replaced. To edit the single-cell array formula for Cum Plan, I select cell B2, click inside the Formula Bar, replace D5:D18 with $A$2>=A5:A18, then press Ctrl+Shift+Enter to get:

{=SUM(($A$2>=A5:A18)*(B5:B18))}

Since cell A2 contains the TODAY() Function, the following formula works as well:

{=SUM((TODAY()>=A5:A18)*(B5:B18))}

The formula’s calculation progression is similar to the previous screen-shots of the Evaluate Formula dialog box.

Single-Cell Array Evaluate Formula

The TODAY() Function evaluates to the date serial number 40490, which is November 8, 2010, and is compared to each cell in the array A5:A18 to get an array of TRUE and FALSE values. The second array B5:B18 is expanded to show the numerical values. These two arrays are multiplied together to get a single array of values, which the SUM Function then evaluates and returns the value 2000 to cell B2.

A single-cell array formula can also be applied to the Act Plan for cell C2, which is:

{=SUM((TODAY()>=A5:A18)*(C5:C18))}

And those are my single-cell array formulas.

Note on Excel for Mac

Apparently there is no Evaluate Formula option in Excel 2008 or 2011 for Mac. I have both programs and it simply doesn’t exist. One of the help forum answers suggested using the F9 key, when editing a formula, to show the values in an array, but that doesn’t work for me. back

Hide or Show Names in Excel with VBA

If you’ve discovered how useful Names can be in a spreadsheet, you may also know they can get-in-the-way-if-you-have-too-many. Just check out the Name Box in the picture to the right.

Each Name has a Visible property that’s set to TRUE by default. You can hide a Name by changing the Visible property to FALSE, but only in the VBA Editor.

Hide Names in Excel

If you have a great many names to hide, it’s best to loop through the Names collection and change the Visible property value to TRUE for all Names.

Sub HideAllNames()
Dim n As Name
For Each n In ThisWorkbook.Names
If n.Visible Then n.Visible = False
Next n
End Sub

After running this code, you now have a blank Name Box.
Excel Name List Blank

If you just want to hide a few Names, the code below, executed in the Immediate Window of the VBA Editor, will do the trick. Just change out “MachName” for each Name you want to hide.

Hide Name with VBA Code in Immediate Window

Go To Hidden Names

You can still Go To a Name that is hidden, just type a Name in the Name Box and hit enter.

Show Hidden Name with Name Box

Or use the keyboard shortcut Ctrl+G or F5 to bring up the Go To dialog box, type a Name in the Reference text box, and click OK.

Show Hidden Name with GoTo Dialog Box

If the Name’s corresponding range address is on a hidden worksheet, this won’t work.

Show Names in Excel

While working on a file, it can sometimes be advantageous to actually SEE ALL the Names. The following VBA code will do just that.

Sub ShowAllNames()
Dim n As Name
For Each n In ThisWorkbook.Names
If n.Visible = False Then n.Visible = True
Next n
End Sub

Keep Some Names Visible with Code

Sometimes you may want to leave a few of the Names visible for users. In the code below I’m looping through the Names collection, setting the Visible property to False, then checking to see if any have “Lookup” in the Name property string and making those Visible.

Sub ShowSomeNames()
Dim n As Name
For Each n In ThisWorkbook.Names
n.Visible = False
If InStr(1, n.Name, "Lookup", vbTextCompare) > 0 Then
n.Visible = True
End If
Next n
End Sub

I use the VBA Function InStr, which stands for In String, to check and see if the Name Property of each Name has “Lookup” contained in it, by starting at character 1. If the InStr Function finds “Lookup” with in the text, it returns a numerical value, which represents the character position of where “Lookup” starts within the text string. If the InStr function doesn’t find “Lookup” within the Name Property, it returns a zero (0).

In the code above, if the InStr function finds “Lookup” within the Name Property string, the code sets the Visible property to TRUE. Here’s what shows in the Name Box after running this code.

Excel Name List INSTR Lookup

You can open up the Excel VBA Editor, create a new module, copy any of the first two code snippets, paste into the module, and it should run just fine. The third snippet is more specialized and would need modification of the “Lookup” attribute for it to work.

My apologies for not indenting the code above, but I’m still learning HTML and CSS.

Related Posts Plugin for WordPress, Blogger...