Sei sulla pagina 1di 155

Learn

Excel VBA in 24 Hours - A quick reference for beginners


Copyright Liaw Hock Sang 2016
All rights reserved.
No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording, scanning, or otherwise, without prior written permission.
For permission requests, please write to: liawhocksang@gmail.com

Trademarks and Copyrighted Content
Every effort has been made to appropriately capitalize all terms that are known to be trademarks or service marks
mentioned in this book. The author cannot attest to the accuracy of this information. Use of a term in this book should
not be regarded as affecting the validity of any trademark or service mark. Should there be any violations in this respect,
the author apologies and shall stop the selling of the book Learn Excel VBA in 24 Hours - A quick reference for
beginners within the control of the author.
Microsoft, Office, Excel, Word, Access, Outlook, Visual Basic, Internet Explorer, and Windows are either registered
trademarks or trademarks of Microsoft Corporation in the United States and/or other countries. All other trademarks
and trade names are the property of their respective owners.

Warning and Disclaimer
Every effort has been made to make this book as accurate as possible. However, no warranty or fitness is implied. The
author and the publisher shall have neither responsibility nor liability to any person or entity with respect to any losses or
damages arising from the information contained in this book.

Thank you
Thank you for purchasing this book. This book is for your personal use only. It may not be resold or given away to other
people. If you are reading this book and did not purchase it, please destroy it. Thank you for your support and respecting
the hard work of the author.

A supplementary Excel file to share
The author would like to share a supplementary Excel file. It contains almost all code listings and code statements stated
in the book. Sub procedures are ready to be executed either by clicking buttons on worksheets or by accessing the Marco
dialog box. Functions are ready to be tested in worksheet formulas. The file definitely facilitates you to master the
content of the book. Please download the file from here. If you cannot download the file, kindly notify the author to
update and republish the link.

Table of Contents
Table of Contents
Introduction
A supplementary Excel file to share
Chapter 1: Knowing your tools
Developer tab
Visual Basic Editor
Customizing VBE
Immediate window
Exercise 1-1: Executing a comparison statement in the Immediate window
Exercise 1-2: Entering a value into a cell from the Immediate window
Object Browser
Exercise 1-3: Exploring the properties of an object
Exercise 1-4: Exploring the properties and methods of an object
Macro recorder
Exercise 1-5: Recording a macro
VBA Help system
Exercise 1-6: Checking the syntax for a structure
Exercise 1-7: Exploring the VBA Help system in Excel 2007 and 2010
Chapter 2: Knowing the places to store your VBA code
Types of VBA modules
Sample procedures in a standard VBA module
A simple Sub procedure: Color banding
Exercise 2-1: Executing a Sub procedure by using the Macro dialog box
Exercise 2-2: Executing a Sub procedure by using a shortcut key combination
Exercise 2-3: Making a Sub procedure available to all opened workbooks
A simple Sub procedure: Hiding the cells contents
Exercise 2-4: Executing a Sub procedure by using the Macro dialog box
A simple Function procedure: A power-of-three function
Exercise 2-5: Using a custom function in a worksheet formula
Exercise 2-6: Calling a custom function from the Immediate window

Exercise 2-7: Calling a custom function by a procedure in other module


A sample procedure in a workbook module
A simple event handler: Workbook_Open
Sample procedures in a sheet module
A simple event handler: Worksheet_SelectionChange
A simple event handler: CommandButton1_Click()
A sample procedure in a UserForm module
A simple UserForm: Collecting information to build a multiplication table
Exporting and importing a VBA module
Chapter 3: Programming fundamentals in VBA
Data types
Constants and variables
Declaring constants and variables
Scope of constants and variables
Exercise 3-1: Testing a static variable
Syntax for decision structures
If-Then structure
If-Then-Else structure
Select Case structure
Syntax for loop structures
For-Next loop
For Each-Next loop
Do loop
Working with strings
Two types of string variables
Comparing strings
Comparison operators
The Like operator
The StrComp function
VBA built-in string functions
Working with arrays

One dimensional arrays


Multidimensional arrays
Dynamic arrays
Function and Sub procedures
Declaring procedures
Scope of procedures
A function with no argument
Exercise 3-2: Using a custom function as a worksheet function
Exercise 3-3: Calling a custom function by a procedure from other module
A function with one argument
Functions with an optional argument
Exercise 3-4: Executing a custom function from the Immediate window
Exercise 3-5: Using a custom function (with a built-in constant) as a worksheet
function
A function with two optional arguments
A function with an array argument
A function with an arbitrary number of arguments
Exercise 3-6: Using SimpleSum1 and SimpleSum2 as worksheet functions
Exercise 3-7: Using SimpleSum3 as a worksheet function
A function that returns an array of values
Exercise 3-8: Executing the MySplit function from the Immediate window
Exercise 3-9: Using MySplit as a worksheet function
Exercise 3-10: Another way of using MySplit as a worksheet function
Exercise 3-11: Returning a two-dimensional array into a range of cells
Debugging
Locals window
Watch window
Call stack
A debugging example from scratch
The MsgBox function
Error handling
A sample procedure with no error handler

Sample procedures with an error handler


Sample procedures with two error handlers
On-Error-Resume-Next and On-Error-GoTo-line statements
Two On-Error-GoTo-line statements
Handling errors in error-handling routines
A fatal error in an error-handling routine
Overcoming fatal errors with the Resume statement
Overcoming fatal errors with a called Sub procedure
Chapter 4: Some other tips on VBA programming
Writing more efficient VBA code
Working with arrays instead of a range of cells
Disabling screen updating, alert displays, events, and automatic calculations
Reducing the size of a working range
Commenting out a block of VBA code
Removing all comments in VBA modules in one click
Getting a list of VBA built-in functions in the code window
Dealing with the situation when Auto List Members does not work
Calling a private procedure from other module
Hiding public Sub procedures from the Marco dialog box
Some good programming practices

Introduction
Visual Basic for Applications (VBA) is the programming language built into Microsoft
Office applications (such as Microsoft Excel, Microsoft Word, Microsoft Access, and
Microsoft Outlook) to automate various tasks in their own environment and to work with
other applications.
This book focuses on VBA in Microsoft Excel for Windows. The discussions in the book
are applicable to Excel 2007, Excel 2010, Excel 2013, and Excel 2016, unless otherwise
stated. It starts with Chapter 1, which is to get you familiarize with some essential tools
that help you to write your VBA programs. Chapter 2 covers the places where you should
store your VBA code. Chapter 3 is the fundamentals of programming in VBA. It provides
you a quick reference in writing syntax error-free VBA code, in debugging, and in
handling errors. Chapter 4 offers some other tips on VBA programming.
This book is for readers that are totally new to Excel VBA, but should have a basic
understanding of programming language, such as C or Python. The reader should be an
average Excel user who knows, for example, what an array formula is. If you are new to
Excel, please teach yourself Excel before exploring the chapters. You may refer to another
book written by me entitled Learn Microsoft Excel 2010-2016 for Windows in 24
Hours - A jumpstart to be an intermediate user, which was written for those who are new
to Excel.
This book does not serve as a comprehensive reference for intermediate users.
Nevertheless, it is a jumpstart for beginners to learn Excel VBA. It is also served as a
foundation for my future books and other references that are beyond the reach of
beginners.
I hope this book will shorten your time to teach yourself Excel VBA and serve as a quick
reference in writing error-free and working VBA programs in realizing your goal. Let
Excel VBA work for you.

A supplementary Excel file to share


The author would like to share a supplementary Excel file. It contains almost all code
listings and code statements stated in this book. Sub procedures are ready to be executed
either by clicking buttons on worksheets or by accessing the Marco dialog box. Functions
are ready to be tested in worksheet formulas. The file definitely facilitates you to master
the content of the book. Please download the file from here. If you cannot download the
file, kindly notify the author to update and republish the link.

Chapter 1: Knowing your tools


Developer tab
By default, the Developer tab in the Excel Ribbon is hidden. Most often when writing
programs, you need to access some of the commands on this tab. Hence, it is essential to
turn it on by executing the steps in the following table:
Version

Steps
1. Click the File tab and choose Options to display the Excel Options
dialog box.

Excel 2010,
Excel 2013, or
Excel 2016

2. Click the Customize Ribbon tab.


3. Under Customize the Ribbon (and with Main Tabs selected), tick the
Developer check box.
4. Click OK to close the dialog box.
1. Click the Microsoft Office button and click Excel Options to display
the Excel Options dialog box.

Excel 2007

2. Click the Popular tab.


3. Under the title Top options for working with Excel, tick the check
box labelled Show Developer tab in the Ribbon.
4. Click OK to close the dialog box.


Note: Later in the book, when accessing a command on the Ribbon or on a menu bar, I
will just state choose File | Options, for example.

Visual Basic Editor


Visual Basic Editor (VBE) is the place where you develop your VBA programs. To
activate VBE, choose Developer | Code | Visual Basic or press Alt+F11.
VBE contains a menu bar, several toolbars, and a number of windows such as the Project
Explorer window, the Properties window, the code window, the Immediate window, the
Object Browser window, the Locals window, and the Watch window. See Figure 1-1. To
identify these windows one by one, click View on the menu bar and select appropriate
items from the drop-down menu of View.



















Figure 1-1: Components in Visual Basic Editor (VBE).

As a beginner, you need not to know the purpose of every component in VBE, but it is
sufficient to know some commonly used features in the editor, which are to be discussed
in this chapter, and appreciate more their purposes as you read and test the VBA code in
this book and as you start to write some programs.

Customizing VBE
In VBE, choose Tools | Options from the menu bar to display the Options dialog box. In
the Editor tab, make sure all check boxes are ticked. After sometimes writing VBA code
and knowing the programming syntax, you may want to unselect the Auto Syntax Check
option.
The Auto List Members and Auto Quick Info options are particular useful. While you type
the name of an object followed by a dot (also known as a period) in the code window and
in the Immediate window, Auto List Members allows a list of members for that object to
be displayed automatically. You can then use the up- and down-arrow keys and the Tab
key to select an item from the list.
If you type the name of a function, property, or method followed by an opening
parenthesis, Auto Quick Info allows the information about the arguments available for the
function, property, or method to be displayed automatically.
In the Editor Format tab, you can see the default colors for text in the code window. The
table below shows some of them.
Text in
window

the

code

Default color

Normal code

Black

Keywords

Blue

Comments

Green

Syntax errors

Red

All other settings in VBE are just right by default.


Immediate window
The Immediate window is most useful to execute a single code statement and get the result
immediately, without having to create a procedure. I discuss procedures later in Chapter 3.

Exercise 1-1: Executing a comparison statement in the Immediate window
Type the following statement, for example, in the Immediate window and press enter to
get the immediate result for a logical comparison:

? 2>3
It returns False.

Exercise 1-2: Entering a value into a cell from the Immediate window
Type the following statement, for example, in the Immediate window and press enter:
ActiveCell = Hello
The statement above enters the word Hello into an active cell in a worksheet of a
workbook. At any particular time, only one workbook, one worksheet of the workbook,
and one cell in the worksheet can be active. Press Alt-F11 to switch to Excel and see the
result in the worksheet.

Object Browser
The Object Browser window allows you to browse a complete list of classes, the
properties, methods, and events in the classes, and VBA built-in constants in any object
libraries. When you are working with other applications (such as Microsoft Word and
Internet Explorer), the libraries associated with those applications need to be included in
your VBA project by setting references. I discuss events and the way of setting references
later in Exercise 3-2 of Chapter 3. Working with other applications is only discussed in my
future book for intermediate users. In addition, Object Browser also shows the procedures
written by you.

Exercise 1-3: Exploring the properties of an object
To browse the properties of an object, for example the Font object, execute the following
steps:
1. In VBE, choose View | Object Browser from the menu bar or press F2 to display
Object Browser.
2. In the Search box of Object Browser, type Font and press Enter. See Figure 1-1.
The Search Results window displays any items that match the search text, in this
case, Font.
3. In the list box of the Search Results window, you can see that Font is a class under
the Class header, and Font is also a member of other classes under the Member
header. If you select any Font under the Member header, its class is then displayed
in the Classes window. Select the Font class in the Search Results window.
4. In the Classes window, Font is selected. Under the Members of Font header, the
members of the Font class are displayed. In the case of the Font class, all members

are properties. For other class, such as the Worksheet class, it can have properties,
methods, and events as its members. Select Underline.
In the bottom pane, it shows more information about the selected item, in this case,
Underline.
5. Press F1 to access a help page about this member.
If to access a help page about the class itself, press F1 when the class is selected
either in the Classes window or in the Search Results window. Help pages are in the
VBA Help system, which will be discussed later in this chapter.

Exercise 1-4: Exploring the properties and methods of an object
To browse the properties and methods of an object, for example the Range object, execute
the following steps:
1. In VBE, choose View | Object Browser from the menu bar or press F2 to display
Object Browser.
2. In the Search box of Object Browser, type Range and press Enter.
The Search Results window displays any items that match the search text, in this
case, Range.
3. In the Classes window, the classes are arranged alphabetically. Look for the Range
class and click on it.
4. Under the Members of Range header, you can see the Font property is one of its
members. There are many other properties and methods. Clear is one of the
methods.
5. Press Alt+F11 to activate Excel.
6. Enter some text into cell B5 and do some formatting to the cell.
7. Press Alt+F11 to activate VBE.
8. In the Immediate window, type the following statement and press Enter to see how
the Clear method clears everything in cell B5.
Range(B5).Clear
The statement above shows a typical way of referencing a member of an object by
using a dot (also known as a period).

Macro recorder
The macro recorder converts your Excel actions into VBA code. Although it does not
always generate the most efficient code, but it is an extremely useful tool that provides
you clues on how to proceed your coding and it allows you to discover the VBA built-in
constants, the properties and methods of objects that you are not familiar with.

Exercise 1-5: Recording a macro


Execute the following steps to record your Excel actions in changing the font of a cell.
1. Start a blank workbook.
2. Choose Developer | Code | Record Macro to display the Record Macro dialog box.
3. In the box labelled Store macro in, choose This Workbook.
4. Click OK to accept other default settings.
By clicking OK, a VBA module is created in VBE. It is named Module1, by default.
5. Press Alt+F11 to activate VBE.
6. In the Project Explorer window, double-click the newly created module Module1
to activate its code window.
7. Resize and place both Excel and VBE windows side by side, so that you can watch
later how Excel actions in Steps 8 10 below are recorded in VBE lively.
8. Select cell B5 or any cell in a worksheet in the Excel window.
9. Choose Home | Font | Bold and Underline, and select the Red font color.
10. Type the word Hello into the selected cell and press Enter.
11. Choose Developer | Code | Stop Recording or alternatively click the square icon on
the status bar to stop the recording.
The Excel actions in Steps 8 10 were converted into VBA code by the macro recorder
and appeared in the code window of Module1. The following listing is the VBA code.
Sub Macro1()

Macro1 Macro

Range(B5).Select
Selection.Font.Bold = True
Selection.Font.Underline = xlUnderlineStyleSingle
With Selection.Font
.Color = -16776961
.TintAndShade = 0
End With
ActiveCell.FormulaR1C1 = Hello
Range(B6).Select
End Sub
The generated lines of code are known as a macro, which is a Sub procedure. Even though
it is not the most efficient code, but it does provide clues on how a program in changing
the font of a cell can possibly be written and allows you to know the Range and Font
objects, the Selection, Font, Bold, Underline, Color, TintAndShade, ActiveCell, and
FormulaR1C1 properties, the Select method, and the xlUnderlineStyleSingle built-in
constant.
The macro can be improved by removing the unnecessary actions of selecting cells and
the default value of the TintAndShade property:
Range(B5).Font.Bold = True
Range(B5).Font.Underline = xlUnderlineStyleSingle
Range(B5).Font.Color = -16776961
Range(B5).FormulaR1C1 = Hello
Its efficiency can be further improved by using the With-End With construct. The
construct reduces the number of dot references that VBA needs to process.
With Range(B5).Font

.Bold = True
.Underline = xlUnderlineStyleSingle
.Color = -16776961

End With
Range(B5).FormulaR1C1 = Hello

VBA Help system


The VBA Help system contains valuable and detailed information about objects and topics
on VBA programming. The system provides context-sensitive help. When you are writing
a VBA code statement in the code window or in the Immediate window, you can get
specific help about a particular object, property, method, constant, keyword, or syntax by
just typing the word and pressing F1.
In Excel 2013 and 2016, you must be connected to the Internet to access the help system.
If you are not connected to the Internet, you can still access a small part of the help system
by downloading a help file from Microsoft Download Center. The latest version that I
downloaded was a help file named Excel 2013 Developer Documentation.chm. However,
the downloaded file is not linked to VBE to provide context-sensitive help.
In Excel 2007 and 2010, the VBA Help system is stored in your local drive. It provides
context sensitive help, without a need to connect to the Internet.

Exercise 1-6: Checking the syntax for a structure


For example, to check the syntax for the Select Case structure, execute the following
steps:
1. Type Select Case in the code window.
2. Press F1 to get more information related to Select Case.
You can find the syntax, an example on how to use the Select Case structure, and
other information on the help page.

Exercise 1-7: Exploring the VBA Help system in Excel 2007


and 2010
You can find lots of information in the VBA Help system by exploring the table of
contents and by searching related words using the search box.
For example, to learn more about operators in Excel VBA, execute the following steps:
1. In VBE, press Alt+H twice to display the Help system.
2. In the Search box, type Operator and press Enter.
3. In the Search results pane, click Operator Precedence.
4. In the left pane, which is a table of contents, a list of topics about operators is
under a book icon named Operators.

5. Click each of these topics for more information.

Chapter 2: Knowing the places to store your VBA


code

Types of VBA modules


A VBA module is a container for storing your VBA code. It can be one of the following
types:
A standard VBA module: It holds VBA code for macros created by the macro
recorder and procedures written by you. The procedures in the module can be called
by procedures in other modules (including the other four types of module that to be
discussed below) if they are declared as public by using the Public keyword. I
discuss procedures later in Chapter 3.

A workbook module: By default, it is named ThisWorkbook. One workbook has
only one workbook module. It holds VBA code for workbook-level events. An event
is an action initiated either by users or by other VBA code which triggers VBA to
execute an event Sub procedure (also known as event-handler procedure or simply
event handler). An event handler is written by you. Opening a workbook, before
closing a workbook, changing a selection on a worksheet, and clicking a command
button are examples of events.
A workbook module can also hold VBA code for other procedures written by you.

A sheet module: It holds VBA code for event handlers that response to the events
associated with a particular sheet in a workbook. Each sheet (either a worksheet or a
chart sheet) has its own sheet module. A sheet module can also hold VBA code for
other procedures.

A UserForm module: A UserForm is a custom dialog box that you build to collect
information from a user. A UserForm module contains a UserForm and event
handlers that response to the events related to the UserForm. It can also hold VBA
code for other procedures. In a workbook, it can have more than one UserForm
module, with one UserForm in each of these modules.

A class module: It is used to create your own new class a template to create an
instance of an object at runtime. The VBA code used to define properties, methods,
and events for a new class is stored in this module. I consider class module is an
advanced topic and it is beyond the scope of this book.
In this chapter, you are going to see some sample procedures in each of these modules
(except the class module). They are only simple procedures and of course many more can

be created in those modules. Nevertheless, the samples do provide you ideas to place your
VBA code in the right modules, and provide you the reasons why the code listings in this
book are stored in certain modules. If you study some sample code listings from other
references, such as books and the Internet, you should find no exception in where VBA
code should be stored.

Sample procedures in a standard VBA module


A simple Sub procedure: Color banding
Suppose that you want to apply alternate color banding to a selected range of cells.
Execute the following steps:
1. Start a blank workbook.
2. Press Alt+F11 to activate VBE.
3. Add a new standard VBA module to the project of the workbook by executing the
following steps:
a. In the Project Explorer window, right-click the projects name (by default, it
is named VBAProject).
b. Choose Insert | Module from the shortcut menu.
By default, it is named Module1. You can change its name in the Properties
window. If the Properties window is not visible, press F4.
4. In the Project Explorer window, double-click the newly added modules name to
activate its code window.
5. Enter the following Sub procedure in the code window:
Sub CreateAlternateColorBanding()
To alternately color rows within a selection
Dim rw As Range
For Each rw In Selection.Rows
If rw.Row Mod 2 <> 0 Then _
rw.Interior.ColorIndex = 15
Next rw
End Sub
Note: An underscore character (preceded by a space) is to separate a single long
code statement that does not fix on a single line in this book. Separating a single
long statement into a few lines also makes the code easier to read, without a need to
scroll horizontally in the code window.

Exercise 2-1: Executing a Sub procedure by using the Macro dialog box
After storing the CreateAlternateColorBanding procedure in a standard VBA module,
execute the following steps to run the sample procedure:

1. Press Alt+F11 to activate Excel.


2. Select a range of cells of at least 2 rows by 2 columns.
3. Press Alt+F8 to display the Macro dialog box.
4. Select the macro named CreateAlternateColorBanding, and click Run.

Exercise 2-2: Executing a Sub procedure by using a shortcut key combination
A faster way to run a Sub procedure is to assign it a shortcut key combination or to create
a custom control on the Ribbon. I discuss the latter in my future book for intermediate
users. The former can be used easily done by executing the following steps:
1. In Excel, press Alt+F8 to display the Macro dialog box.
2. Select the macro CreateAlternateColorBanding, and click the Options button to
display the Macro Options dialog box.
3. Assign your shortcut key combination by pressing Shift+B, for example, and click
OK to close the Macro Options dialog box.
The shortcut key combination is then Ctrl+Shift+B.
4. Click Cancel to close the Macro dialog box.
5. Select a range of cells and test the procedure again by pressing the newly assigned
shortcut key combination.

Exercise 2-3: Making a Sub procedure available to all opened workbooks
If to make a Sub procedure available for other workbooks and not just for the workbook
that stores the VBA code for that procedure, you may store the code in your Personal
Macro Workbook. This workbook is named Personal.xlsb, as you can see in the Project
Explorer window.
If Personal.xlsb does not exist, execute the following steps to create the workbook.
1. In Excel, choose Developer | Code | Record Macro to display the Record Macro
dialog box.
2. In the box labelled Store macro in, choose Personal Macro Workbook and click
OK to accept other default settings.
By clicking OK, the Personal Macro Workbook is created in VBE.
3. Choose Developer | Code | Stop Recording or alternatively click the square icon on
the status bar to stop recording a dummy macro in the newly created Personal Macro
Workbook.
4. Press Alt+F11 to activate VBE.
5. In the code window, delete the dummy macro, but retain its VBA module in the

Project Explorer window for later use.


To make CreateAlternateColorBanding available for all opened workbooks, copy the Sub
procedure and paste it into the code window of the VBA module in the Personal Macro
Workbook. You can also assign it a shortcut key combination, as discussed in Exercise 2-2
above.

A simple Sub procedure: Hiding the cells contents


Suppose that you want to store some information in certain cells, but the information is
not relevant to users. Hence, you want to hide it. Enter the following Sub procedure in an
existing or a new standard VBA module:
Sub HideCellContent()

Selection.NumberFormat = ;;;
End Sub

Exercise 2-4: Executing a Sub procedure by using the Macro dialog box
After storing the HideCellContent procedure in a standard VBA module, execute the
following steps to run the sample procedure:
1. In a worksheet, enter something (text, numbers, or formulas) into a few cells.
2. Select the cells with something entered in Step 1.
3. Press Alt+F8 to display the Macro dialog box.
4. Select the macro named HideCellContent, and click Run to hide the contents.
The contents of the selected cells are not actually totally hidden. By selecting one of
the cells, you can still view its contents in the formula bar.

A simple Function procedure: A power-of-three function


A standard VBA module not only can store Sub procedures, but also Function procedures.
They are known as user-defined or custom functions. The simple function below is to
demonstrate that a custom function in a standard VBA module can be used as a worksheet
function in a worksheet, just like other Excel worksheet functions, and can be used in
other modules, just like other VBA built-in functions.
Enter the following Function procedure in an existing or new standard VBA module:

Function Power3(x As Double) As Double

Power3 = 3 ^ x
End Function

Exercise 2-5: Using a custom function in a worksheet formula
To use the preceding sample custom function in a worksheet formula, execute the
following steps:
1. Press Alt+F11 to activate Excel.
2. Select any cell in a worksheet and enter the following formula, for example:
=Power3(4)
It returns 81, which is 3 to the power of 4.

Exercise 2-6: Calling a custom function from the Immediate window
To call the function from the Immediate window, enter the following statement, for
example:
? Power3(2)
It returns 9, which is 3 to the power of 2.

Exercise 2-7: Calling a custom function by a procedure in other module
A custom function (or a procedure, in general) stored in a standard VBA module can be
called not only by procedures in its own module, but also by procedures in other modules
if it is declared as a public function (or public procedure, in general). These other modules
can be other standard VBA modules, workbook modules, sheet modules, UserForm
modules, and class modules.
To call the custom function Power3 (which is stored in a standard VBA module) from
other module, execute the following steps:
1. Add a standard VBA module to the project of the workbook that stores the custom
function.
Note: Calling a custom function by procedures in other workbooks will be discussed
in Exercise 3-2.
2. In the code window of the newly added module (or in the code window of the
workbook module or a sheet module), enter the following Sub procedure:
Sub TestPower3()

Debug.Print Power3(4)
End Sub
The Debug.Print method executes the expression Power3(4) and sends the output of
the execution to the Immediate window.
If to call the custom function from the workbook module and a sheet module, copy
and paste the TestPower3 procedure into those modules.
3. Press F5 or click the triangle icon on the Standard toolbar to execute the
TestPower3 procedure.
In this case, it returns 81 in the Immediate window.

A sample procedure in a workbook module


By default, the workbook module in a workbook is named ThisWorkbook. Each Excel file
has only one workbook module.

A simple event handler: Workbook_Open


Suppose that you want to limit the scroll area in a particular worksheet of a workbook
when the workbook is opened. Execute the following steps:
1. Start a blank workbook, or open an existing workbook.
2. Press Alt+F11 to activate VBE.
3. In the Project Explorer window, double-click ThisWorkbook to activate the code
window of the workbook module.
4. In the code window, select Workbook from the Object drop-down list, which is
located on the top-left corner of the code window. See Figure 1-1.
5. By default, the Open event is automatically selected from the Procedure dropdown list, which is located on the top-right corner of the code window.
Try to select other events from the Procedure drop-down list. Once a new item is
selected, an empty event handler of the selected event is inserted into the code
window.
6. To set the scroll area for a particular worksheet (say, the first worksheet) of the
workbook to the range A1:N20, enter the following VBA code into the code
window:
Private Sub Workbook_Open()
Worksheets(1).ScrollArea = A1:N20
End Sub
7. Save the workbook as a macro-enabled XLSM file in Excel 2010 2016 or XLM
file in Excel 2007.
8. Close and reopen the workbook.
9. If you are prompted with a security warning about active or macro content, you
need to enable the content. Otherwise, the Workbook_Open event-handler procedure
and any other procedures to be written by you will not work.
To clear the scrolling limit, execute the following statement in the Immediate window:
Worksheets(1).ScrollArea =
An alternative to set and clear the scrolling limit for a particular worksheet without using

VBA code is by changing the ScrollArea property in the Properties window of the
worksheet. In VBE, press F4 (or in Excel, choose Developer | Controls | Properties) to
display the Properties window.

Sample procedures in a sheet module


Each sheet (either a worksheet or a chart sheet) has its own sheet module. The event
handlers in a sheet module only handle events associated with that particular sheet.

A simple event handler: Worksheet_SelectionChange


Suppose you are entering data into a worksheet. You want to keep the row of an active cell
(the cell where data is to be entered) to be the second row from the top of the worksheet.
To accomplish this task, execute the following steps:
1. Start a blank workbook, or open an existing workbook.
2. Press Alt+F11 to activate VBE.
3. Under a folder named Microsoft Excel Objects in the Project Explorer window,
double-click the name of a worksheet that you want to enter data into.
This will activate the code window of the sheet module of the worksheet.
4. In the code window, select Worksheet from the Object drop-down list, which is
located on the top-left corner of the code window.
5. By default, the SelectionChange event is automatically selected from the
Procedure drop-down list, which is located on the top-right corner of the code
window.
Try to select other events from the Procedure drop-down list. Once a new item is
selected, an empty event handler of the selected event is inserted into the code
window.
6. To keep the row of an active cell to be the second row from the top of the
worksheet, enter the following VBA code into the code window:
Private Sub Worksheet_SelectionChange( _
ByVal Target As Range)
If Target.Row > 1 Then _
ActiveWindow.ScrollRow = Target.Row - 1
End Sub
7. Press Alt+F11 to active Excel.
8. Click any cell in that worksheet and start entering data into a few cells to see the
effect.

A simple event handler: CommandButton1_Click()


Suppose that you want to create a button on a worksheet and by clicking the button, a Sub
procedure is executed. To accomplish this task, execute the following steps:
1. Start a blank workbook, or open an existing workbook.
2. In Excel, select the worksheet that you want to place the button.
3. Choose Developer | Controls | Insert and select Command Button (ActiveX
Control).
4. Draw the button on the worksheet.
Command buttons have default names: CommandButton1, CommandButton2
5. Double-click the command button to activate the code window of the sheet
module.
Observe the item selected from the Procedure drop-down list, which is located on
the top-right corner of the code window. By default, the Click event is selected.
6. Enter the following VBA code into the code window:
Private Sub CommandButton1_Click()
MsgBox Hello world!
End Sub
An event of clicking the command button will trigger the execution of the
CommandButton1_Click event handler.
7. Press Alt+F11 to activate Excel and choose Developer | Controls | Design Mode to
exit design mode.
Or in VBE, choose Run | Exit Design Mode from the menu bar.
8. Click the command button on the worksheet to call the event handler.
You may replace the MsgBox statement with a statement of calling a procedure, for
example:
Call CreateAlternateColorBanding
Or
Application.Run CreateAlternateColorBanding
CreateAlternateColorBanding is the procedure that was discussed right before
Exercise 2-1.

A sample procedure in a UserForm module


A UserForm is a custom dialog box with necessary controls (label, text box, combo box,
list box, check box, option button, toggle button, frame, command button, scroll bar, spin
button, image, multipage, and some other less commonly used controls) placed on it to
collect information from users.

A simple UserForm: Collecting information to build a


multiplication table
Suppose that you want to create a multiplication table on a worksheet based on a number
selected by a user. Figure 2-1 shows an example of a UserForm to collect information
from the user.


Figure 2-1: A Userform to collect information from a user.

To create the UserForm, execute the following steps:
1. Start a blank workbook, or open an existing workbook.
2. Press Alt+F11 to activate VBE.
3. Add a UserForm module to the project of the workbook by executing the following
steps:
a. In the Project Explorer window, right-click the projects name of the
workbook.
b. Choose Insert | UserForm from the shortcut menu.
By default, it is named UserForm1. You can change its name in the Properties
window. If the Properties window is not visible, press F4.

A blank UserForm is then created with a floating Toolbox window. If the Toolbox
window is not visible, choose View | Toolbox from the menu bar.
4. In the Properties window of the UserForm,
a. Change the Caption property to Create Multiplication Table.
b. Change the Name property to MTableUF.
If the Properties window is not visible, press F4.
5. Add a frame, three option buttons, and a command button to the UserForm by
choosing the controls from Toolbox and drawing the controls on the UserForm. See
Figure 2-1.
You may use the buttons on the UserForm toolbar to align and distribute the
controls. If the toolbar is not visible, choose View | Toolbars | UserForm from the
menu bar.
6. Set the following properties of the controls, as listed in the table below:
Control/Property Name

Caption Accelerator

Frame

Frame1

Select a
number

NA

Option button 1

Option3

Option button 2

Option4

Option button 3

Option5

Command button

CreateButton Create

The Accelerator property emulates an action of clicking a control by using a key


combination. For example, a user can press Alt+3 as if the option button Option3 is
clicked.
7. To set one of the option buttons as a default selected button when the Userform is
shown, set its Value property to True.
8. To set the command button as the default command button that receives the
keystroke of Enter, set its Default property to True.
To create an event handler for an event of clicking the command button on the UserForm
and to create a Sub procedure for generating a multiplication table, execute the following
steps:
1. Double-click the command button on the UserForm to insert an empty event
handler for clicking the command button into the code window of the UserForm
module.
You may use F7 and Shift+F7 to switch between the code window and the

UserForm.
2. Enter the following code for the event handler and Sub procedure in the code
window:
Private Sub CreateButton_Click()
Dim n As Long
If Me.Option3 Then n = 3
If Me.Option4 Then n = 4
If Me.Option5 Then n = 5
Call CreateMTable(n)
Unload Me
End Sub
Note: Me refers to the UserForm object.

Private Sub CreateMTable(n As Long)
To create a multiplication table of n
on an active worksheet
Dim i As Long
With ActiveSheet
For i = 1 To 12
.Cells(i, 1) = _
i & x & n & = & i * n
Next i
.Columns(A:A).AutoFit
End With
End Sub
To create a command button on a worksheet and an event handler for an event of clicking
the command button, execute the following steps:
1. Press Alt+F11 to activate Excel, and select a worksheet.
2. Choose Developer | Controls | Insert and select Command Button (ActiveX
Control).
3. Draw the button on the worksheet.
4. Double-click the command button to activate the code window of the sheet

module.
By default, the Click event is selected from the Procedure drop-down list, which is
located on the top-right corner of the code window.
5. Enter the following event handler into the code window:
Private Sub CommandButton1_Click()
MTableUF.Show
End Sub
6. Press Alt+F11 to activate Excel and choose Developer | Controls | Design Mode to
exit design mode.
Or in VBE, choose Run | Exit Design Mode from the menu bar.
7. Click the command button on the worksheet to show the UserForm.
8. Select an option and click the Create button on the UserForm to generate a
multiplication table.

Exporting and importing a VBA module


To export a VBA module from a workbook, execute the following steps:
1. In the Project Explorer window of the workbook, right-click the module that to be
exported.
2. Choose Export File from the shortcut menu.
3. Locate the folder to save the file, type a filename, and click Save.
The original module actually remains in the project, and only a copy of it is
exported.
To import an exported VBA module, which was saved in a file, into a workbook, execute
the following steps:
1. In the Project Explorer window of the workbook, right-click the projects name.
2. Choose Import File from the shortcut menu.
3. Locate the file containing the VBA module, and click Open.

Another way of exporting and importing a VBA module from one workbook to another is
just simply by opening both workbooks. In the Project Explorer window, drag the module
that to be exported and drop it into the project of the other workbook. The original module
actually remains in the project, and only a copy of it is added to the project of the other
workbook.

Chapter 3: Programming fundamentals in VBA


As mentioned in Introduction, this book assumes that you should have a basics
understanding of programming language, such as C or Python. This chapter recaps the
fundamentals of programming in the environment of Excel VBA.

Data types
A data type, such Integer or Boolean, is a classification of data. It determines the space the
data occupies in memory, the values the data can store, and the operations can be done on
the data. The following table lists the supported data types in Excel VBA with their
storage sizes and value ranges.
Data
type

Storage
size

Range of values

Byte

1 byte

0 to 255

Boolean

2 bytes

True or False

Integer

2 bytes

-32,768 to 32,767

Long

4 bytes

-2,147,483,648 to 2,147,483,647

4 bytes

-3.402823E38 to -1.401298E-45 for negative values;


1.401298E-45 to 3.402823E38 for positive values

8 bytes

-1.79769313486231E308 to
-4.94065645841247E-324 for negative values;
4.94065645841247E-324 to 1.79769313486232E308
for positive values

Currency

8 bytes

-922,337,203,685,477.5808 to
922,337,203,685,477.5807

Decimal

14 bytes

+/-79,228,162,514,264,337,593,543,950,335 with no
decimal point;
+/-7.9228162514264337593543950335 with 28 places
to the right of the decimal

Date

8 bytes

January 1, 100 to December 31, 9999

Object

4 bytes

Any object reference

String
(variablelength)

10 bytes +
string length

0 to approximately 2 billion characters

String
(fixed-length)

Length of
string

1 to approximately 65,400 characters

Variant
(with
numbers)

16 bytes

Any numeric value up to the range of a Double data


type

Variant
(with
characters)

22 bytes +
string length

Same range as for variable-length String

User-defined
(using Type)

Bytes
required by
elements

The range of each element is the same as the range of


its data type.

Single

Double

A Variant is a special data type. If a variable is declared without explicitly stating its data
type, it is then, by default, of data type Variant. A variant variable can hold any kind of
data, including special values such as Empty, Error, Nothing, and Null.

The data type of a variant variable changes when needed, depending on what manipulation
is done on the variable. The following listing illustrates the point by checking the data
type of a variant variable.
Sub CheckVariantDataType()

Dim myvar As Variant


myvar = Null
Debug.Print TypeName(myvar) returns Null
myvar = 3
Debug.Print TypeName(myvar) returns Integer
myvar = myvar & 1
Debug.Print TypeName(myvar) returns String
myvar = myvar * 2
Debug.Print TypeName(myvar) returns Double
Debug.Print myvar returns 62
End Sub
The TypeName function is used to determine the data type of a variable.
VBA also allows you to define custom (also known as user-defined) data types. Often,
custom data types are to group several related variables. The following definition of a
custom data type named uStaffInfo illustrates the point:
Type uStaffInfo

StaffName As String * 30
Addr(1 To 2) As String
Age As Byte
End Type
The definition of a custom data type must be placed at the top of a VBA module before
any procedures.

Constants and variables


A constant is an identifier or a unique name with a value that cannot be altered during an
execution of a program. Constants are preferable to hard-coded values. For example, a
constant named VatRate is better than a hard-coded value, say, of 6 in a program. It makes
the code in the program easier for you to understand and easier to maintain if a need to
change the VatRate constant arises.
On other hand, a variable is an identifier with a value that can be changed during an
execution of a program.

Declaring constants and variables


A declaration is to name a constant or variable and to specify its characteristics, such as
the data type and the scope of visibility.
VBA allows you to use a variable without declaring it. However, it can lead to bugs with
no compilation error, but with unexpected or, even worse, unnoticed wrong results.
Suppose you use an undeclared variable named temp. At some point in your code, you
misspell it as tmp. When the compiler encounters a name that it does not recognize as an
existing variable, it creates a new variable using that name. It does not flag that as an error.
Instead, it will create a new variable named tmp. This can lead to bugs that they are very
difficult to identify.
You may use the Option Explicit statement at the top of a module to force explicit
declarations of all variables in a module. To automatically insert the Option Explicit
statement in a new VBA module, you may enable the Require Variable Declaration option
in the Editor tab of the VBE Options dialog box. (Choose Tools | Options from the menu
bar to display the Options dialog box.)

The following two tables, respectively, show some examples of how constants and
variables are declared.
Examples of declaring constants
To declare an integer constant:
Const RateCol As Integer = 2
To declare a constant without a data type:
Const RateCol = 2

Without explicitly stating a data type, VBA determines
the data type that is most appropriate to the constant.

To declare multiple constants on a single line:


Const Rate = 6.15, Term As Integer = 48
To declare a string constant:
Const AppName As String = Housing Loan


Examples of declaring variables
To declare a variable of data type Boolean:
Dim FileOpened As Boolean
To declare an integer variable:
Dim Counter As Integer
To declare a variable of data type Long:
Dim NextRow As Long
To declare a variable of data type Date:
Dim Timer1 As Date
To declare a variable of data type String:
Dim FileName As String
To declare a string variable with a specified number of
characters:
Dim FileName As String * 20
To declare a variable of data type Object:
Dim objElements As Object
To declare a variable without explicitly stating a data
type:
Dim Temp

The variable is then, by default, of data type Variant.
To declare multiple variables on a single line:
Dim i As Long, j As Long

The variables i and j are of data type Long.
To declare multiple variables on a single line:

Dim i, j As Long

Only j is of data type Long. Since i is in fact declared
without explicitly stating a data type, it is of data type
Variant, by default.
To declare an object variable that can, but not yet,
contain a reference to a worksheet object:
Dim ws As Worksheet

The variable ws holds the special value Nothing until it
is assigned an object reference using the Set statement,
for example:
Set ws = ActiveSheet
To declare an object variable:
Dim coln As Collection

To create an object (using the New keyword) and assign
its reference to an object variable:
Set coln = New Collection

The object reference (to the newly created Collection
object) is assigned to the variable coln using the Set
statement.
A shortcut to declare an object variable, to create an
object, and to assign the reference of the object to the
object variable:
Dim coln As New Collection
To declare a two-dimensional array of integers:
Dim iMatrix(1 To 3, 1 To 4) As Integer
To declare a dynamic array of custom data types:
Dim uCACommittee() As uStaffInfo
To declare a dynamic array of variants:
Dim vMyArray()

I discuss arrays later in this chapter.

Scope of constants and variables


A declared constant or variable can be used either in all modules, in a particular module,
or just in a particular procedure. Its scope of visibility (that is, in where it can be used)
depends on where and how (by using either the Public, Private, Dim, or Static keyword) it
is declared. The following two tables, respectively, show the scopes of declared constants
and variables.
Location where
a constant is
declared
Before the first
procedure in a
module

Example of how a
constant is declared

Scope of
visibility

Public Const Dept As


String = DXC

All modules


Private Const Dept As
String = DXC

Before the first


procedure in a
module

All
procedures
Const Dept As String = in that
module
DXC


Within a
procedure (after
the Sub or
Function
statement)

Const Dept As String =


Only in that
DXC
procedure

Location where
a variable is
declared

Example of how a
variable is declared

Scope of
visibility

Before the first


procedure in a
module

Public myStr As String

All modules

Before the first


procedure in a
module

Private myStr As
String

Dim myStr As String

Within a
procedure (after
the Sub or

Dim myStr As String


All
procedures
in that
module

Only in that
procedure

Function
statement)

Static myStr As String

The Public and Private keywords can only be used before the first procedure in a module.
All public constants and variables declared in a VBA module with the Public keyword can
be used in all modules. If they are declared in a non-standard VBA module (such as a
sheet or workbook module), to use them from other modules you need to precede their
names with the module name. For example, a public variable named myStr declared in a
worksheet module named Sheet1 can be accessed with the following expression:
Sheet1.myStr
A module-level constant, a constant that is declared in a module before the first procedure,
without using the Private keyword is private to the module by default. A module-level
variable either declared with the Private or Dim keyword is private to the module.
All variables and constants declared within a procedure can only be accessed within the
procedure. A procedure-level variable declared with the Static keyword (the keyword that
can only be used within a procedure to declare variables) retains its value between
procedure calls.

Exercise 3-1: Testing a static variable
To understand more about a static variable, execute the following steps:
1. Enter the following Function procedure into the code window of either a new or an
existing standard VBA module:
Function Counter() As Long
To return the number of times it is called
The variable x preserves its value
between calls
Static x As Long
x = x + 1
Counter = x
End Function
2. Enter the following Sub procedure either into the code window of the module
where the Counter function is stored in or into the code window of other module:
Sub TestCounter()
Dim i As Long
For i = 1 To 4
Debug.Print Counter()

Next i
End Sub
3. Choose Run | Run Sub/UserForm or press F5 to execute the Sub procedure.
Observe the values displayed in the Immediate window. The static variable x (in the
Counter function) retains its value.
4. In the Immediate window, enter the following statement to call the function
another time:
? Counter()
It returns 5. That is, the fifth time the function is called.
5. Press Alt+F11 to activate Excel, and enter the following formula into any cell:
=Counter()
It returns 6. That is, the sixth time the function is called.

Syntax for decision structures


VBA supports three decision structures: If-Then, If-Then-Else, and Select Case. This
section lists the syntax followed by one or a few examples for each structure.

If-Then structure
The following are two variations of an If-Then structure followed by one or two simple
examples for each variation.
Syntax for an If-Then structure
If condition Then statement(s)
Example 1
If A > 3 Then Cnt = Cnt + 1
Example 2
If A > 3 Then Cnt = Cnt + 1 : Sum = Sum + A
Note: A colon is to place two statements on a single line.


Syntax for an If-Then structure
If condition Then
[statement(s)]
End If
Note: A pair of square brackets indicates an optional part of
the structure.
Example
If A > 3 Then

Cnt = Cnt + 1
Sum = Sum + A
End If

If-Then-Else structure

The following are two variations of an If-Then-Else structure followed by a simple


example for each.
Syntax for an If-Then-Else structure
If condition Then
[statement(s)]
Else
[statement(s)]
End If
Example
If A > 3 Then

Cnt = Cnt + 1
Else

B = B + 1
End if


Syntax for an If-Then-Else structure
If condition1 Then
[statement(s)]
ElseIf condition2 Then
[statement(s)]
Else
[statement(s)]
End If
Example
If Score >= 8 Then

MsgBox A
ElseIf Score > 3 And Score < 8 Then

MsgBox B
Else

MsgBox C
End If


Alternatively, the preceding structure that uses ElseIf can be replaced with the following
structure, which is more intuitive and easier to understand.
Syntax for an If-Then-Else structure
If condition1 Then
[statement(s)]
Else
If condition2 Then
[statement(s)]
Else
[statement(s)]
End If
End If
Example
If Score >= 8 Then

MsgBox A
Else

If Score > 3 And Score < 8 Then


MsgBox B
Else
MsgBox C
End If
End If

Select Case structure


When the conditions in an If-Then-Else structure are referring to the same test expression,

a Select Case structure is a better alternative. It offers much clearer code.


Syntax for a Select Case structure
Select Case TestExpression
Case Expression1
[statement(s)]
[Case Expression2
[statement(s)]]
[Case else
[statement(s)]]
End Select
Example 1
Select Case Score

Case Is >= 8
MsgBox A
Case 4 To 7
MsgBox B
Case Else
MsgBox C
End Select
Example 2
Select Case Round(Rate, 2)

Case Is < 0.50: MsgBox Low


Case 0.50 To 2.49: MsgBox OK
Case 2.50 To 2.99: MsgBox KIV
Case Else: MsgBox High
End Select
Example 3
Select Case TypeName(myVar)

Case Empty: MsgBox Empty


Case Integer: MsgBox Integer
Case String: MsgBox String
Case Double: MsgBox Double
End Select

TestExpression can be a numeric expression or string expression.


VBA allows multiple expressions (separated by commas) and ranges in each Case clause,
for example:
Case 0 To 90, 125, 180 To 270, 325, Is > 360
If TestExpression matches more than one Case expression, only the statements following
the first match are executed. Hence, the order of the Case expressions does matter. The
following listing illustrates the concept.
Sub CatchAlphanumeric()

Dim myChar As Variant


myChar = InputBox(Enter a character: )

If Len(myChar) <> 1 Then


MsgBox More than a character was entered.
Exit Sub
End If
Select Case UCase(myChar)
Case 0 To 9
MsgBox A digit.
Case A, E, I, O, U
MsgBox A vowel.
Case A To Z

Vowels were tackled in previous Case.


Hence, it does not matter to have
them again.
MsgBox A consonant.
Case Else
MsgBox Not a digit nor a letter.
End Select
End Sub
If the case of vowels (A, E, I, O, U) is placed after the case of alphabet (A to Z), then the
vowel case will never be matched.

Syntax for loop structures


VBA supports three loop structures: For-Next, For Each-Next, and Do-Loop. This section
lists the syntax followed by one or a few examples for each loop structure.

For-Next loop
A For-Next loop executes a group of statements for a specified number of times.
Syntax for a For-Next loop
For counter = start To end [Step step]
[statement(s)]
[Exit For]
[statement(s)]
Next [counter]
Example 1
Dim i As Long
For i = 2 To 10 Step 2

Debug.Print i, returns 2 4 6 8 10
Next
Example 2
Dim i as Long
For i = 9 To 1 Step -2

Debug.Print i, returns 9 7 5 3 1
Next i

The optional Exit For statement in a For-Next loop is often used after evaluating of some
condition, for example in an If-Then structure, and control is then transferred to the
statement immediately after the For-Next loop.

For Each-Next loop

A For Each-Next loop executes a group of statements for each element in a collection.
Syntax for a For Each-Next loop
For Each element In group
[statement(s)]
[Exit For]
[statement(s)]
Next [element]
Example
Dim ws As Worksheet
For Each ws In Worksheets

Debug.Print ws.Name
Next

Do loop
A Do loop is either a Do-While loop or a Do-Until loop. Unlike For-Next and For EachNext loops that execute for a specified number of times, a Do loop executes as long as the
While condition in the loop is True or the Until condition in the loop becomes True.
Syntax for a Do-While loop
Do [While condition]
[statement(s)]
[Exit Do]
[statement(s)]
Loop
Example
Dim i As Long: i = 2
Do While i <= 10

Debug.Print i, returns 2 4 6 8 10
i = i + 2
Loop

Syntax for a Do-Until loop


Do [Until condition]
[statement(s)]
[Exit Do]
[statement(s)]
Loop
Example
Dim i As Long: i = 2
Do Until i > 10

Debug.Print i, returns 2 4 6 8 10
i = i + 2
Loop


If to execute at least once the statements inside a Do loop, simply move the While or Until
condition of the Do loop and place it right after the Loop keyword, as shown below.
Syntax for a Do-While loop that executes at least once
Do
[statement(s)]
[Exit Do]
[statement(s)]
Loop [While condition]
Example
Dim i As Long: i = 99
Do

Debug.Print i, returns 99
i = i + 2
Loop While i <= 10


Syntax for a Do-Until loop that executes at least once
Do
[statement(s)]

[Exit Do]
[statement(s)]
Loop [Until condition]
Example
Dim i As Long: i = 99
Do

Debug.Print i, returns 99
i = i + 2
Loop Until i > 10

The optional Exit Do statement in a Do loop is often used after evaluating of some
condition, for example in an If-Then structure, and control is then transferred to the
statement immediately after the Do loop.

Working with strings


Two types of string variables
There are two types of string variables in VBA:
A fixed-length string: A string variable that you declare with a specified number of
characters, for example:
Dim strName As String * 30
If you assign the strName variable with characters less than 30, VBA fills the extra
positions with spaces. If you assign strName with characters greater than 30, only
the first 30 characters are assigned to the variable, and the rest are ignored.
The maximum length of a fixed-length string is 65,535 characters.
A dynamic string: A string variable that you declare without stating any specified
number of characters, for example:
Dim strName As String
It can store up to 2 billion characters.

Comparing strings
There are three ways to compare a pair of strings: comparison operators (<, <=, >, >=, =,
and <>), the Like operator, and the StrComp function.
The Option Compare statement appeared at the top of a VBA module before any
procedures specifies the string comparison method (Binary for case-sensitive
comparisons, Text for case-insensitive comparisons, or Database only be used within
Microsoft Access) in the module. If a module does not have the Option Compare
statement, the default comparison method is Binary.

Comparison operators
Type the following statements into the Immediate window and press Enter after each.
? Amy < Ann returns True
? Amy <= Ann returns True
? Amy = Ann returns False
? Amy > Ann returns False
? Amy >= Ann returns False
? Amy <> Ann returns True

? Amy < Ann returns False


? Amy < Amy Bond returns True
VBA performs a character-by-character comparison between two strings.

The Like operator
The Like operator allows you to use wildcards, character lists, and character ranges in
comparing strings, not just fixed characters. The allowed wildcard characters are ?, *,
and #. A character list is a list of characters in a pair of square brackets. A character
range is specified by a stating character, a hyphen character (-), and an ending character.
The following two tables clarify the explanation.
Character

What it matches in a string

Any single character

Zero or more characters

Any single digit (09)

[CharacterList]

Any single character in CharacterList

[!CharacterList]

Any single character not in


CharacterList


Example statement using the
Like operator

The statement returns


true if

MyStr Like [A-Za-z0-9]????#

MyStr is six-characters
long, starts with an
alphanumeric character
and ends with a digit.

W 48a Like [A-Z] ##[a-z]

The first character is a


capital letter, the second
is a space, the next two
are digits, and the fifth is
a lowercase letter.

MyStr Like [AEIOUaeiuo]*[*]

MyStr starts with a vowel


and ends with an asterisk
character.

MyStr Like [!AEIOUaeiuo]*

The first character in


MyStr is not a vowel.


The StrComp function
The syntax for the StrComp function is as below:
StrComp(string1, string2[, compare])
Similar to comparison operators, the StrComp function performs a character-by-character
comparison between two strings. The third argument in the function provides an option to
ignore the Option Compare statement in a module. The return values of the StrComp
function are as shown in the following table.
Condition

Return value

string1 < string2

-1

string1 = string2

string1 > string2

string1 or string2 is Null

Null

VBA built-in string functions


VBA has a lot of useful built-in functions when dealing with strings. Knowing their
functionalities, you can then incorporate the functions in your VBA code.
After you type the name of a function (followed by an opening parenthesis) in the code
and Immediate windows, the information about its arguments is automatically displayed.
If VBE does not display the information, enable the Auto Quick Info option in the Editor
tab of the VBE Options dialog box. (Choose Tools | Options from the menu bar to display
the Options dialog box.)
The next table shows some commonly used VBA string functions. The table lists the
syntax for each function, the common functionality of the function, and a simple example.
Consult the VBA Help system for a complete description of the syntax.
Function

Syntax | What it returns | Example

Returning the length of a string


Len(string | varname)
Len

The number of characters in a string.


Len(This is an example) returns 18.

Returning the position of a substring in a string

InStr([start, ]string1, string2[, compare])


InStr

The start position of the first occurrence of a


substring in a string.
InStr(This is an example, is) returns 3.
InstrRev(stringcheck, stringmatch [, start[,
compare]])

InStrRev

The position of the first occurrence of a


substring in a string, starting from the right
side of the string.
InStrRev(This is an example, is) returns 6.

Returning a manipulated string


Left(string, length)
Left

A string containing a specified number of


characters from the left side of a string.
Left(This is an example, 5) returns This .
Right (string, length)

Right

A string containing a specified number of


characters from the right side of a string.
Right(This is an example, 5) returns
ample.
Mid(string, start[, length])
A string containing a specified number of
characters from a string.

Mid

Mid (This is an example, 5, 3) returns is.


Note: If to extract a substring starting in the
middle of a string, until the end of the string,
omit the third argument.
LTrim(string)

LTrim,

A string with no leading spaces.


LTrim( 2s and 3 ) returns 2s and 3 .
RTrim(string)

RTrim

A string with no trailing spaces.


RTrim( 2s and 3 ) returns 2s and 3.
Trim(string)
A string with no leading and trailing spaces.

Trim

Trim( 2s and 3 ) returns 2s and 3.

Note: To remove all spaces except for single


spaces,
use
Application.WorksheetFunction.Trim().
LCase(string)
LCase

A string converted to lowercase.


LCase(She is Ann) returns she is ann.
UCase(string)

UCase

A string converted to uppercase.


UCase(She is Ann) returns SHE IS ANN.
StrConv(string, conversion, LCID)

StrConv

A string converted to a specified form.


StrConv(SHE IS ANN, vbProperCase)
returns She Is Ann.
Space(number)
A string with a specified number of spaces.

Space

Hi & Space(3) & ! returns Hi !.


Note: The ampersand (&) characters are to
join multiple strings into a single string.
String(number, character)

String

A string with a repeating character.


String(3, *) returns ***.
StrReverse(expression)

StrReverse A string in reversed order.


StrReverse(AbcDe) returns eDcbA.
Replace(expression, find, replace [, start[,
count[, compare]]])
A string in which a specified substring has
been replaced with another substring.
Replace(This is an example, s, S) returns
ThiS iS an example.
Replace


Note: The Replace function has six arguments
and other functionalities. For example, if to
replace only the first occurrence of a specified
substring in a string, set the fifth argument to 1,
as shown in an example below:
Replace(a12012b, 12, B, , 1)returns

aB012b.
CStr(expression)
Convert an expression to a string.
CStr

CStr(12.3) returns 12.3.


Note: Check the Help system for other typeconversion functions.
Format(expression[,format [,firstdayofweek
[,firstweekofyear]]])

Format

A string formatted according to a format string


expression.
Format(9/8/2016, dd mmm yy) returns08
Sep 16.

Returning and joining an array of strings


Split(expression[, delimiter[, limit[,
compare]]])
A zero-based, one-dimensional array
containing a number of substrings.
Split

Split(This is an example, ) returns an


array containing four strings: This, is,
an, and example.
Note: If the second argument is omitted, a
space character ( ) is assumed to be the
delimiter.
Join(sourcearray[, delimiter])

Join

A string created by joining a number of


substrings contained in an array.
Join(Split(This is an example), |) returns
This|is|an|example.
Filter(sourcesrray, match[, include[,
compare]])
A zero-based, one-dimensional array
containing a subset of a string array based on
specified filter criteria.

Filter

Join(Filter(Split(This is an example),a),|)
returns
an|example.
The Filter function in the example above
returns an array of two elements: an and
example, in which both have the character

a.
Returning a character
Chr(charcode)
A character associated with a character code.
Chr

Chr(65) returns A.
Note: To return a character code associated
with a character, use Asc(). For example:
Asc(A) returns 65.

Working with arrays


An array is a group of elements that are of the same data type and have a common array
name. Each element in an array can be referred by using the array name and an index (for
a one-dimensional array) or several indexes (for a multidimensional array).

One dimensional arrays


There are two ways of declaring an array with a specific number of elements in the array:
one with both lower and upper indexes, and the other with only the upper index.
Dim strWeekDays(1 To 7) As String
Dim strWeekDays(6) As String
The two arrays above consist 7 elements each. Without stating the lower index, by default,
VBA assumes it to be zero. If to make VBA to assume that 1 is the lower index, include
the Option Base 1 statement at the top of a VBA module before any procedures. To avoid
any confusion, just include both lower and upper indexes when declaring arrays.
In fact, the lower index needs not to be 0 or 1. It can be other positive and negative
integers.
The following listing shows an example of how to declare a one-dimensional array, to
assign a value to an array element in the array, and to retrieve the value.
Sub OneDArrayDemo1()
Declare an integer array of 7 elements

Dim intScales(-2 To 4) As Integer


Assign an integer 50 to 2nd element in the array
by using the index that refers to the element

intScales(-1) = 50
Check the assigned value

Debug.Print intScales(-1) returns 50


End Sub
The next listing shows an example of a one-dimensional array of custom data types.
Define a custom data type
Type StaffInfo

StaffName As String * 30
Addr(1 To 2) As String
Age As Integer
End Type

Sub OneDArrayDemo2()
Declare a 50-elements array of custom data types

Dim cusStaff(1 To 50) As StaffInfo



Assign values to an element in the array

With cusStaff(1)
.StaffName = Richard
.Addr(1) = 1, Wall Street
.Addr(2) = New York
.Age = 35
End With

Check the assigned values


Debug.Print cusStaff(1).StaffName returns Richard
Debug.Print cusStaff(1).Addr(2) returns New York
End Sub

Multidimensional arrays
A multidimensional array can be two, three, or more dimensions. The way to access each

element in a multi-dimensional array is by using the array name and the indexes of its
respective dimensions.
To check the lower and upper indexes for each dimension of an array, the LBound and
UBound functions are used. The following two listings show examples of how to check
the indexes for two and three dimensional arrays, respectively:
Sub MultiDimArrayDemo1()
Declaring a 4-by-5 two-dimensional
array of integers

Dim a(3, 4) As Integer


Checking the upper and lower indexes
for 1st dimension of a()

Debug.Print LBound(a, 1), returns 0


Debug.Print UBound(a, 1) returns 3
for 2nd dimension of a()

Debug.Print LBound(a, 2), returns 0


Debug.Print UBound(a, 2) returns 4
End Sub

Sub MultiDimArrayDemo2()
Declaring a 10-by-4-by-8 three dimensional array of variants

Dim b(1 To 10, 0 To 3, -3 To 4)


Checking the upper and lower indexes
for 1st dimension of b()

Debug.Print LBound(b, 1), returns 1


Debug.Print UBound(b, 1) returns 10
for 2nd dimension of b()

Debug.Print LBound(b, 2), returns 0


Debug.Print UBound(b, 2) returns 3
for 3rd dimension of b()

Debug.Print LBound(b, 3), returns -3


Debug.Print UBound(b, 3) returns 4
End Sub

Dynamic arrays
If the number of elements in an array that you want to declare is only known at runtime,
you may declare it as a dynamic array without stating the upper index (and the lower
index), for example:
Dim a() As Integer
Dim b() As Integer
You can later use the ReDim keyword to tell VBA the number of elements in the array, for
example:
ReDim a(2 To x)
ReDim b(1 To x, -3 To y)
where x and y are the values that are only known at runtime.
ReDim can be used for any number of times to change the number of elements in an array.
When it is used, the existing values in the array are destroyed. If to preserve the values,
you can use ReDim Preserve, such as:
ReDim Preserve a(2 To x + 5)
ReDim Preserve b(1 To x, -3 To 8)
However, you can resize only the last dimension of a dynamic array and can change only
its upper index.
The following listing is an example of how a dynamic array is resized in a For-Next
structure while preserving the values in the array.
Sub DynamicArrayDemo1()

Dim dynArry() As Long, i As Long

For i = 1 To 5
ReDim Preserve dynArry(1 To i)
dynArry(i) = i * 2
Next

Display the values in the array


For i = LBound(dynArry) To UBound(dynArry)
Debug.Print dynArry(i), returns 2 4 6 8 10
Next i
End Sub
It is not always a must that one needs to use the ReDim statement before a declared
dynamic array can store values, as shown in the following example:
Sub DynamicArrayDemo2()
Store values without ReDim

Dim Words() As String


Words() = Split(This is an example)
Debug.Print Join(Words, |)
End Sub
After execution of the statement Words() = Split(This is an example), the Words array
becomes a zero-based, one-dimensional array containing four elements: This, is, an,
and example. The Join(Words, |) statement returns This|is|an|example.

Function and Sub procedures


A procedure is either a Function procedure or a Sub procedure (also known as subroutine
or macro). It contains lines of code to accomplish a specific task.
A Function procedure written by you is a custom function. It must return a single value or
an array of values. It can possibly be used as a worksheet function and as a function in
other procedures.
A Sub procedure, on the other hand, does not return any value. Nevertheless, procedures
can emulate return values if their arguments (also known as parameters) are passed by
reference (ByRef).

Declaring procedures
Declaring a procedure is to name the procedure and to specify its characteristics, such as
the data type and the scope of accessibility.
The syntax for a Function procedure:
Function Name ([ArgumentsList]) [As Type]
[statement(s)]
[Name = expression]
[Exit Function]
[statement(s)]
[Name = expression]
End Function

The syntax for a Sub procedure:
Sub Name ([ArgumentsList])
[statement(s)]
[Exit Sub]
[statement(s)]
End Sub
Name is the name of a procedure. The rules for naming procedures are the same as the
ones in naming arguments in the procedures, constants, and variables. A valid name must
begin with a letter. It can contain digits and underscore characters (_), but cannot
contain spaces, arithmetic operator symbols, punctuation marks, other special characters

such as ~, `, @, #, $, %, ^, &, =, |, \, <, and >.


ArgumentsList is a list of arguments that is passed to a procedure when it is called.
Arguments are separated by commas. By default, arguments are passed by reference
(ByRef). To pass an argument by value, precede the argument with the ByVal keyword.
However, a custom data type can only be passed by reference.
Type is the data type of the return value by a Function procedure. It can be Byte, Boolean,
Integer, Long, Currency, Single, Double, Date, String, Object, Variant, or a custom data
type.

Scope of procedures
A procedure can be called by procedures in any modules or only by procedures in the
module that stores the procedure. Its scope of accessibility (that is, from where it can be
called) depends on how (by using either the Public or Private keyword) it is declared, as
shown in the following table.
An example of how a procedure is
declared

Can be called
by

Public Sub UpdateImage()



Sub UpdateImage()
Procedures in
Public Function Power3(x As Double) As any modules
Double


Function Power3(x As Double) As Double
Private Sub UpdateImage()

Procedures in
the module that
Private Function Power3(x As Double) As stores the
procedure
Double

A procedure declared without explicitly using the Public keyword is public by default.
If a public procedure is stored in a standard VBA module, it can be called by procedures
in any modules using the Call keyword, for example Call UpdateImage. If it is a Function
procedure, it can also be used as a worksheet function, just like other Excel built-in
worksheet functions.
A public Sub procedure in a standard VBA module can be executed in one of the
following ways:
Use the Macro dialog box. See Exercise 2-1.

Press the shortcut key combination that is assigned to the procedure. See Exercise
2-2.
In VBE, press F5 to run it.
Call it from the Immediate window. For example, to execute a Sub procedure
named CreateAlternateColorBanding, enter the following statement in the
Immediate window:
Call CreateAlternateColorBanding
Or
Application.Run CreateAlternateColorBanding
Call it by a procedure in other module. The way to call is the same as the one from
the Immediate window. For example,
Call CreateAlternateColorBanding
Or
Application.Run CreateAlternateColorBanding
Click a command button. If the command button is a form control, the Assign
Macro dialog box is automatically displayed after you draw the button on a
worksheet. In the dialog box, you can assign a Sub procedure to the button. If the
command button is an ActiveX control, please consult the topic A simple event
handler: CommandButton1_Click() in Chapter 2.
A public Function procedure in a standard VBA module can be executed in one of the
following ways:
Use it as a worksheet function. See Exercise 2-5.
Call it from the Immediate window. See Exercise 2-6.
Call it by a procedure in other module, as if it is a VBA built-in functions. See
Exercise 2-7.
If a public procedure is in a non-standard VBA module (such as a sheet or workbook
module), it can be called by procedures in any modules using the Call keyword and the
name of the module that stores the procedure, for example Call Sheet1.UpdateImage. If it
is a Function procedure, it cannot be used as a worksheet function.
A public Sub procedure either in a standard or non-standard VBA module can be executed
in the same ways, with one exception. In a statement calling a procedure in non-standard
module, the procedure name must be preceded by the module name, for example:
Call Sheet1.CreateAlternateColorBanding
Or
Application.Run Sheet1.CreateAlternateColorBanding
Often, when you want a procedure to be only accessible by the procedures in the module

storing the procedure, you declare it as a private procedure. However, VBA provides a
way to call a private procedure from other module. Since the way distracts the main
purpose of declaring a procedure private, it is only discussed later in Chapter 4 under the
topic Calling a private procedure from other module.
Literally there are countless of Function and Sub procedures. In the following discussion,
you will see some sample functions: from a function with no argument to a function with
an arbitrary number of arguments, from a function that returns a single value to a function
that returns an array of values, and functions with optional arguments. The sample
functions provide you the programming syntax on how to write those functions.
The discussion on the sample Function procedures applies also to Sub procedures, with
the only difference that Sub procedures do not return a value or an array of values.

A function with no argument


A function can take no argument and its return data type depends on the purpose the
function is created.
For example, the following ThisFilePath function takes no argument and returns the
complete path of an active workbook:
Function ThisFilePath() As String

ThisFilePath = ActiveWorkbook.Path
End Function

Exercise 3-2: Using a custom function as a worksheet function
As mentioned earlier, to use a custom function as a worksheet function, the function must
be stored in a standard VBA module and declared as public. To use the custom function
ThisFilePath as a worksheet function, execute the following steps:
1. Start a blank workbook, or open an existing workbook.
2. Press Alt+F11 to activate VBE.
3. Insert a new or use an existing standard VBA module.
4. Enter the VBA code for the custom function in the code window of the module.
5. Press Alt+F11 to activate Excel.
6. Save the workbook as a macro-enabled XLSM file in Excel 2010 2016 or XLM
file in Excel 2007.
7. Select any cell in a worksheet of the newly saved workbook and enter the

following formula into the cell:


=ThisFilePath()
The formula returns the path where the workbook is saved.
8. To use the function as a worksheet function in other workbook, there are two ways:
a. Include the complete path before the function name:
=C:\VBA\Book1.xlsm!ThisFilePath()
Book1.xlsm is the Excel file that stores the custom function.
Before the function can be used in other workbook, the workbook that stores
the function must first be opened.
b. Set a reference
i. To avoid name conflicts with other VBA projects, change the
projects name of the custom function from VBAProject to
LearnVBAProject or other unique name in the Properties window of the
project. If the Properties window is not visible, press F4.
ii. In the Project Explorer window, select the projects name of the
workbook that will use the function by clicking.
iii. Choose Tools | References from the menu bar to display the
References dialog box.
iv. Click Browse to display the Add Reference dialog box.
v. Choose Microsoft Excel Files from the file type box.
vi. Locate the workbook file storing the function, in this case
Book1.xlsm.
vii. Click Open and then OK to close the References dialog box.
When a reference is successfully established, there is a reference node in
the Project Explorer window.
To use the function, you can now just enter the following formula into any cell
without a need to include the complete path:
=ThisFilePath()
Similarly, when you use Excel VBA to work with other applications such as
Microsoft Word and Internet Explorer, you need to set references to the
libraries associated with those applications.

Exercise 3-3: Calling a custom function by a procedure from other module
To call the function ThisFilePath from other modules in the same workbook that stores the
function, execute the following steps:

1. Open the workbook containing the custom function.


See Steps 1 - 4 in Exercise 3-2 on how to store the function in a standard VBA
module.
2. In VBE, insert a new standard VBA module, or use an existing sheet module or the
workbook module.
3. In the code window of the module, enter the following Sub procedure:
Sub TestThisFilePath()
Debug.Print ActiveWorkbook.Name & _
is stored in & ThisFilePath
End Sub
4. To execute the Sub procedure, choose Run | Run Sub/UserForm or press F5.
5. Observe the output in the Immediate window.
If the Immediate window is not visible, press Ctrl+G.

If to execute a custom function from other workbook, you need to set a reference to the
workbook that stores the function, as discussed in Step 8b, Exercise 3-2.

A function with one argument


The following is an example of a function with one argument. The function takes a
filename with (or without) the complete path as its argument and returns a filename
without the path.
Function FileNameOnly(FullName As String) As String
e.g.: C:\temp\BookA.xlsx becomes BookA.xlsx

FileNameOnly = Mid(FullName, _
InStrRev(FullName, \) + 1)
End Function
This function can be used as a worksheet function and can be called by a procedure in any
module. To test the function, consult Exercises 3-2 and 3-3 above.

Functions with an optional argument


A function can have one or more optional arguments. If an optional argument is not
provided when a function is called, the VBA code defining the function uses a default
value. The default value is either implicitly stated in the VBA code for the function or
explicitly stated in the argument list.
Optional argument(s) can only be placed after any required argument(s).
The following function shExist takes two arguments: one required and one optional. It
returns True if a sheet exists in a workbook. A sheet can be a worksheet, chart sheet,
macro sheet, or dialog sheet. The default value for the optional argument is
ActiveWorkbook, which is implicitly stated in the VBA code for the function.
Function shExist(shName As String, _

Optional wb As Workbook) As Boolean


Dim Dummy As String
If wb Is Nothing Then Set wb = ActiveWorkbook
On Error Resume Next
Dummy = wb.Sheets(shName).Name
On Error GoTo 0
If Dummy <> Then
shExist = True
Else
shExist = False
End If
End Function
This function can be called by a procedure in any module. To use the function as a
worksheet function, leave the second argument blank. To test the function, consult
Exercises 3-2 and 3-3 above.
The following is another example of a function with one optional argument. The
CountRepeat function returns the number of occurrences of a substring within a search

string and provides an option to a user whether the search is case-sensitive or caseinsensitive. The default value for the optional argument is vbTextCompare, which is
explicitly stated in the argument list of the function.
Function CountRepeat(MyString As String, _

MySubStr As String, Optional _


Compare As VbCompareMethod _
= vbTextCompare) As Long
If Len(MySubStr) > 0 Then _
CountRepeat = _
(Len(MyString) - Len(Replace( _
MyString, MySubStr, , , , Compare))) _
/ Len(MySubStr)
End Function
This function can be used as a worksheet function and be called by a procedure in any
module. To test the function, consult Exercises 3-4 and 3-5 below.

Exercise 3-4: Executing a custom function from the Immediate window
After storing the CountRepeat function in a standard VBA module, execute the following
steps to call the function from the Immediate window:
1. In VBE, press Crtl+G to display the Immediate window.
2. In the Immediate window, type the following fragment of a statement:
? CountRepeat(
The information about the arguments for the function is automatically displayed. If
VBE does not display the information, enable the Auto Quick Info option in the
Editor tab of the VBE Options dialog box. (Choose Tools | Options from the menu
bar to display the Options dialog box.)
3. Type the first two arguments, for example:
? CountRepeat(abc Bb,b,
A list of choices (vbBinaryCompare, vbDatabaseCompare, and vbTextCompare) for
the third argument is automatically displayed. If VBE does not display the list,

enable the Auto List Members option in the Editor tab of the VBE Options dialog
box.
4. Use the up- and down-arrow keys and the Tab key to select an item in the list. For
example, the following statement returns 2:
? CountRepeat(abc Bb,b,vbBinaryCompare)

Exercise 3-5: Using a custom function (with a built-in constant) as a worksheet function
After storing the CountRepeat function in a standard VBA module, execute the following
steps to use the function in a worksheet formula:
1. Enter the following formula into any cell:
=CountRepeat(abc Bb,b)
It returns 3 correctly since the search of b in abc Bb is case-insensitive.
2. Enter the following formula into any cell:
=CountRepeat(abc Bb,b,vbBinaryCompare)
It returns an error because Excel does not recognize the VBA built-in constant:
vbBinaryCompare.
3. Use Object Browser to check the value of the built-in constant, or simply type the
following statement in the Immediate window to find out its value.
? vbBinaryCompare
It returns 0.
4. Enter the following formula into any cell:
=CountRepeat(abc Bb,b,0)
It then returns 2 correctly since the search of b in abc Bb is case-sensitive.
5. If cell A1 contains the text abc Bb, and cell A2 contains the text b, you may
test the function by entering the following formula into any other cells:
=CountRepeat(A1,A2,0)
It returns 2 correctly.
When a custom function has several arguments and it is used as a worksheet function, to
remember what to enter for each of these arguments is not easy. You may then press
Ctrl+Shift+A after typing an equal sign and the functions name into a cell (for example,
=CountRepeat) to display the names of the arguments.

A function with two optional arguments

The following shows a fictitious function with two optional arguments. Even though it is
fictitious, but at least it shows how an argument list of a function with any number of
optional arguments looks like.
Function AddAllDemo(arg1 As Long, _

Optional arg2 As Long, _


Optional arg3 As Long = 10) As Long
AddAllDemo = arg1 + arg2 + arg3
End Function
Arguments in a procedure can be passed in several different ways. Table below shows a
few ways in passing the arguments of the AddAll function when it is called from the
Immediate window (or equivalently from a VBA module) and when it is used as a
worksheet function. The default values for the first and second optional arguments are
implicitly zero and explicitly ten, respectively.
Way of passing arguments
In the Immediate
window, enter the
following statement

In a worksheet,
enter the following
formula into a cell

Return
value

All 3 arguments supplied


? AddAllDemo(5,6,7)

=AddAllDemo(5,6,7) 18

First two arguments supplied


? AddAllDemo(5,6)

=AddAllDemo(5,6)

21

Second argument omitted


? AddAllDemo(5,,7)

=AddAllDemo(5,,7)

12

Using named arguments


?
AddAllDemo(arg1:=5, Not applicable
arg3:=7)

12

A function with an array argument

A function can take not only single values, but also an array of values and arrays of values
as its arguments. Arrays are always passed by reference (ByRef). They cannot be passed
by value (ByVal).
Consider a function that takes only an array of values as its argument. In declaring the
function, there is no way to state the dimension of the array argument, for example:
Function MyArrayFunction(Arry() As Double)

End Function
However, you can restrict the dimension of the array when working with the VBA code in
the function. The table below shows how to restrict the dimension of an array argument to
one and two, and how to allow the array argument to accept an array of any dimensions.
A function with a one-dimensional array argument
Function SumAllArray1D(Arry() As Double) As Double

Dim i As Long
SumAllArray1D = 0
For i = LBound(Arry) To UBound(Arry)
SumAllArray1D = SumAllArray1D + Arry(i)
Next i
End Function
A function with a two-dimensional array argument
Function SumAllArray2D(Arry() As Double) As Double

Dim i As Long, j As Long


SumAllArray2D = 0
For i = LBound(Arry) To UBound(Arry)
For j = LBound(Arry, 2) To UBound(Arry, 2)
SumAllArray2D = SumAllArray2D + Arry(i, j)
Next j
Next i

End Function
A function with an array argument of arbitrary
dimensions
Function SumAllArrayMD(Arry() As Double) As Double

Dim Item As Variant


For Each Item In Arry
SumAllArrayMD = SumAllArrayMD + Item
Next Item
End Function
Alternatively, you may define the argument as Variant data
type and let VBA decide the most appropriate data type,
such as below:

Function SumAllVariant(Arry As Variant) As Double

Dim Item As Variant


For Each Item In Arry
SumAllVariant = SumAllVariant + Item
Next Item
End Function
The SumAllVariant function can also take a range of cells as
its array argument.

A function with an arbitrary number of arguments


A function can literally take an infinite number of arguments, as shown in the following
fictitious function:
Function ParamArrayFunction(arg1 As Long, ByVal arg2 As Long,

ParamArray ArgList() As Variant) As Long

End Function
The first two arguments in the procedures argument list are required arguments: one is
passed by reference, and the other is passed by value.
The third argument with the ParamArray keyword is the one that enables the function to
take literally an array of an infinite number of arguments. It must be defined as an array of
variants and always be the last argument in the procedures argument list. It cannot be
used with the ByVal, ByRef, and Optional keywords.
The following SimpleSum1 function literally can take an infinite list of numbers in its
argument list.
Function SimpleSum1(ParamArray ArgList()) As Double

Dim i As Long
For i = 0 To UBound(ArgList)
SimpleSum1 = SimpleSum1 + ArgList(i)
Next
End Function
Note: LBound of ArgList() is always 0, regardless of the Option Base statement.
Alternatively, SimpleSum1 can be rewritten as below:
Function SimpleSum2(ParamArray ArgList()) As Double

Dim Item
For Each Item In ArgList
SimpleSum2 = SimpleSum2 + item
Next
End Function
An argument that passed to the SimpleSum1 and SimpleSum2 functions can be a number,
a number in a cell, or a number returned by a function.

Exercise 3-6: Using SimpleSum1 and SimpleSum2 as worksheet functions
After storing the SimpleSum1 and SimpleSum2 functions in a standard VBA module,
execute the following steps to use the functions as worksheet functions:

1. Enter the following formulas separately into any two cells:


=SimpleSum1(1,2,3)
=SimpleSum2(1,2,3)
Both return 6.
2. Enter the following formulas separately into any two cells:
=SimpleSum1(1,2,3,SQRT(4))
=SimpleSum2(1,2,3,SQRT(4))
Both return 8.
3. Enter a number into cell A5 and enter the following formulas separately into any
other two cells:
=SimpleSum1(1,2,3,SQRT(4),A5)
=SimpleSum2(1,2,3,SQRT(4),A5)
Both return the sum of 1, 2, 3, 2, and the numeric value in cell A5.
SimpleSum1 and SimpleSum2 consider only the case if the ParamArray argument is an
array of numbers. If to write a more versatile function that can accept not only an array of
numbers, but also arrays of numbers, such as ranges of cells, you need to tackle the two
cases in the VBA code for the function.
Note: A ParamArray argument can literally take an array of an infinite number of
arguments. An argument in the array can be a number, or a range of cells (which is an
array of cells containing numbers) among many other possibilities of arguments. A
range of cells (such B1:B3) is just one of the arguments, not a list of arguments. A list of
cells (separated by commas for example, B1, B2, B3) is a list of arguments. Ranges of
cells (separated by commas for example, B1:B3, B5:D8, C3:D5) are a list of arguments.
The following SimpleSum3 function considers the cases that the arguments in the array of
an infinite number of arguments are numbers and ranges of cells.
Function SimpleSum3(ParamArray ArgList()) As Double

Dim item, cel As Range


For Each item In ArgList
Select Case TypeName(item)
Case Range
For Each cel In item
SimpleSum3 = SimpleSum3 + cel

Next cel
Case Else
SimpleSum3 = SimpleSum3 + item
End Select
Next item
End Function
The TypeName function is used to determine the data type of the argument in the
argument list of the SimpleSum3 function.

Exercise 3-7: Using SimpleSum3 as a worksheet function
After storing the SimpleSum3 function in a standard VBA module, execute the following
steps to use the function in a worksheet formula:
1. Enter a few numbers separately into a few ranges of cells, for example the ranges
B1:B3 and B5:B7.
2. In any other cell, enter the following formula:
=SimpleSum3(3,SQRT(4),B1:B3,B5:D8)
It returns the sum of 3, 2, and the numeric values in the ranges B1:B3 and B5:D8.

A function that returns an array of values


So far, the sample functions discussed above return only single values. A function can
possibly return an array of values.
The following MySplit function accepts a string of characters as its argument and returns a
zero-based, one-dimensional array containing each character in the string.
Function MySplit(MyString As String) As String()

Dim tmp As String


tmp = StrConv(MyString, vbUnicode)
tmp = Left(tmp, Len(tmp) - 1)

MySplit = Split(tmp, Chr(0))


End Function

Exercise 3-8: Executing the MySplit function from the Immediate window
After storing the MySplit function in a standard VBA module, enter the following
statement in the Immediate window to test the function:
? Join(mysplit(Hello World!),|)
It returns H|e|l|l|o| |W|o|r|l|d|!

Exercise 3-9: Using MySplit as a worksheet function
Since the MySplit function returns an array of string, to use the function in a worksheet
formula, you need to enter the formula as an array formula.
After storing the function in a standard VBA module, execute the following steps to test
the function:
1. Select a range of cells, for example the range A21:L21.
2. Type the following array formula into the formula bar and press Ctrl+Shift+Enter
(not just Enter):
=MySplit(Hello World!)
It returns individual characters of the string Hello World! into the range A21:L21.
3. To return individual characters into a vertical range, select a vertical range of cells,
for example the range M21:M32, and enter the following array formula into the
formula bar by pressing Ctrl+Shift+Enter (not just Enter):
=TRANSPOSE(MySplit(Hello World!))
If you need to edit an array formula, execute the following steps:
1. Select any cell in the array formula range.
2. Press F2 or click the formula bar and make changes to the formula.
3. Press Ctrl+Shift+Enter to accept the changes.
To delete an array formula, select any cell in the array formula range, press Ctrl+/ to select
all the cells in the range and press Delete.

Exercise 3-10: Another way of using MySplit as a worksheet function
You can also enter an array formula into a range of cells by using VBA code. This can be
done by the FormulaArray property of a Range object.

For example, after storing the MySplit function in a standard VBA module, execute the
following steps:
1. Enter the following statement in the Immediate window
Range(A22:L22).FormulaArray = =MySplit(Hello World!)
2. Press Alt+F11 to activate Excel and see the result.

The next function ReturnArryDemo1 returns a 3-by-4 two-dimensional array:
Function ReturnArryDemo1() As Long()

Dim L() As Long


Dim R As Long, C As Long
ReDim L(1 To 3, 1 To 4)
For R = 1 To 3
For C = 1 To 4
L(R, C) = R + C
Next C
Next R
ReturnArryDemo1 = L
End Function

Exercise 3-11: Returning a two-dimensional array into a range of cells
After storing the ReturnArryDemo1 function (which returns a two-dimensional array of
numbers) in a standard VBA module, execute the following steps to use the function in a
worksheet formula:
1. Select a range of 3-by-4 cells, for example the range A25:D27.
2. Type the following array formula into the formula bar and press Ctrl+Shift+Enter
(not just Enter):
=ReturnArryDemo1()
The function returns an array of numbers into the selected cells.

3. Select another range that is larger than 3-by-4 cells, for example the range E25:I29.
4. Type the following array formula into the formula bar and press Ctrl+Shift+Enter
(not just Enter):
=ReturnArryDemo1()
The function not only returns numbers into the selected cells, but also fills out the
unused cells with #N/A errors.
You can then modify ReturnArryDemo1 to fill out the unused cells either with empty
characters or numbers. The following ReturnArryDemo2 function discusses the latter: to
fill out with numbers.
Function ReturnArryDemo2() As Long()

Dim L() As Long


Dim R As Long, C As Long
Dim nR As Long, nC As Long
Determine the size of a selected range
With
nR = .Rows.Count
nC = .Columns.Count
End With
ReDim L(1 To nR, 1 To nC)
For R = 1 To nR
For C = 1 To nC
L(R, C) = R + C
Next C
Next R
ReturnArryDemo2 = L

End Function
The VBA code in the ReturnArryDemo2 function first determines the size of a selected
range. Then, the dimensions of the array (that to be returned) is resized according to the
size of the selected range.

Debugging
Debugging is a process of finding and fixing errors in program code. The following is a
typical way of using the debugger to step through the code and identify errors:
1. Add breakpoints at particular lines of code, where you suspect errors, by clicking
at the grey left margin of the code window.
2. Press F5 to execute the VBA code.
VBE executes the code until the first breakpoint is met, putting the code into break
mode. The particular line of code at the breakpoint is not executed yet.
3. Choose one of the three operations at the breakpoint:
Operation

Shortcut
Action of the debugger
key

Continue

F5

It continues executing until the


next breakpoint.

Step Into

F8

If the current point beak does


not have a procedure (a
Function or Sub procedure) to
be called, it executes the line of
code. Otherwise, it goes into the
procedure. To step out of a
procedure, press Ctrl+Shift+F8.

Step Over

If the current point beak has a


procedure to be called, it treats
Shift+F8 the procedure as a single
instruction, instead of going into
the procedure.

The operations listed in the table above can alternatively be accessed from the
Debug menu on the menu bar.
4. As you step through the code to identify possible errors, you can hover the mouse
pointer over any variable to examine its current value. If VBE does not display the
value, enable the Auto Data Tips option in the Editor tab of the VBE Options dialog
box. (Choose Tools | Options from the menu bar to display the Options dialog box.)
Alternatively, you may use the Immediate window to check the current value of a variable
when the code is in break mode. For example, enter the following statement in the
Immediate window:
? myVariable
In addition, you may use the Locals window and the Watch window (which are the topics
to be discussed next) to monitor the values of variables when debugging your VBA code.

Locals window
The Locals window allows you to observe the current values of all variables in a
procedure that is currently being executed. In VBE, choose View | Locals Window from
the menu bar to display the Locals window. Only when the code is in break mode, the
current values of all variables are displayed. Otherwise, the Locals window is empty.

Watch window
The Watch window allows you to put your VBA code into break mode when certain
condition is met. The condition is based on the value of an expression or a variable that
you set in the Watch window.
To add a variable as a watch to the Watch window, execute the following steps:
1. Right-click any occurrence of the variable, for example a variable named Sum, in
the code window.
2. Choose Add Watch from the shortcut menu to display the Add Watch dialog box.
The Expression box automatically inserts the name of the variable: Sum, in this
case.
3. In the Watch Type group, select one of the three watch types:
Watch Expression: The Watch window simply watches the expression
without stopping code execution. The value of the expression is displayed in
the Watch window only when VBE is in break mode.
Break When Value Is True: Your VBA code is put into break mode when the
value of the watched expression is True. If necessary, you may then change
the watched expression and its watch type.
Break When Value Changes: Your VBA code is put into break mode when
the value of the watched expression changes. If necessary, you may then
change the watched expression and its watch type.
4. Click OK to accept other default settings.
Only when VBE is in break mode, the current values of all watched expressions are
displayed. Otherwise, the values are out of context.
To edit (or delete) a watch, right-click the watch in the Watch window and choose Edit
Watch (or Delete Watch). If the Watch window is not visible, choose View | Watch
Window from the menu bar to display the window.

Call stack
The call stack lists all procedures that are currently running, with the most current
procedure at the top of the list. When VBE is in break mode, choose View | Call Stack
from the menu bar or press Ctrl+L to display the Call Stack dialog box. The dialog box
can only be displayed when there is at least one procedure in break mode.

A debugging example from scratch


1. Start a blank workbook.
2. Press Alt+F11 to activate VBE.
3. In the Project Explorer window, right-click the projects name (by default it is
named VBAProject) and choose Insert | Module from the shortcut menu to insert a
standard VBA module.
4. In the Project Explorer window, double-click the newly created modules name (by
default, it is named Module1) to activate its code window.
5. Enter the following code into the code window:
Option Explicit

Sub DebugDemo()
Dim k As Long
For k = 1 To 3
Call Sub1a
Next k
End Sub

Sub Sub1a()
Dim i As Long, j As Long, Sum As Long
For i = 1 To 2
For j = 1 To 4
Sum = Sum + i * j

Debug.Print Sum
Debug.Assert Sum <> 10
Next j
Next i
End Sub
6. Press Ctrl+G to display the Immediate window.
7. Choose View | Locals Window from the menu bar to display the Locals window.
8. Execute the following steps to add two watches: k changes and Sum = 3 is True.
a) Right-click any occurrence of the k variable in the code window.
b) Choose Add Watch from the shortcut menu to display the Add Watch dialog
box.
c) Type k into the Expression box.
d) In the Watch Type group, select the option Break When Value Changes.
e) Click OK to accept other default settings.
f) Right-click any occurrence of the Sum variable in the code window.
g) Choose Add Watch from the shortcut menu to display the Add Watch dialog
box.
h) Type Sum = 3 into the Expression box.
i) In the Watch Type group, select the option Break When Value Is True.
j) Click OK to accept other default settings.
9. Resize the Immediate, Locals, Watch, and code windows, if needed, so that you
can see all of them.
10. Click anywhere within the DebugDemo procedure to activate it and press F5 to run
it.
The VBA code is put into its first break mode when k changes from 0 to 1. Observe
the changes in the Locals and Watch windows.
11. Press F5 to continue the code execution.
The code is then in its second break mode when the expression Sum = 3 is True.
Observe the changes in the Locals and Watch windows.
The Debug.Print Sum statement of displaying the value of Sum (in this case, 3) in
Immediate window has not been executed yet. Hence, it is not shown in the
Immediate window.
12. Press F5 to continue the code execution.
VBE enters into break mode again when the condition (Sum <> 10) in the

Debug.Assert statement is False. Observe the value of Sum in the Locals window,
which is 10.
An alternative to the Debug.Assert Sum <> 10 statement is to add the Sum variable
as a watch to the Watch window: with the expression in the Expression box entered
as Sum = 10 and with its watch type selected Break When Value Is True.
13. Press Ctrl+L to the display the Call Stack dialog box.
Choose one of the two procedures in the list and click the Show button to jump to
the point of execution in the chosen procedure.
14. Press F5 a few more times to continue the code execution while observing the
changes in the Locals, Watch, and Immediate windows until the code execution
ends.
You can see that the VBA code is put into break mode whenever the k variable
changes, the expression Sum = 3 is True, or the value of the variable Sum is 10.
In a nutshell, you have seen four debugging tools to put VBE into break mode:
Stepping through the VBA code by using the debugger
Setting breakpoints in the VBA code
Adding watches to the Watch window
Inserting the Debug.Assert statements in the VBA code
When VBE is in break mode, the current values of variables can then be examined.

The MsgBox function


The MsgBox function can be used to monitor the value of a specific variable while your
VBA code is executing, by displaying the value on a message box. The execution is
paused until the message box is dismissed. You place the functions at strategic locations in
the code to check whether the variable is holding expected values at those locations.
For example, you are expecting a variable named Sum to have at least reached a value of
10 during the code execution. Hence, you place the following statement at a strategic
location in the code to make sure it really reaches that value:
If Sum = 10 then MsgBox Sum is & Sum & .
The statement above simply displays a message box with an OK button when the value of
the variable is, expectedly, 10. If no message box is displayed throughout the execution,
then something and somewhere in the code must be wrong. To debug further, you may
possibly place more MsgBox functions at other locations, or use the four debugging tools
(that were discussed earlier) to find and fix the error in the code.

Other than as a debugging tool, the MsgBox function is also useful to get a decisive
response from a user. For example, if to let a user decide whether to continue the code
execution, you may display Yes and No buttons on the message box, and subsequently
handle the decision made by the user. The following block of statements shows a fictitious
example, where Sum is a variable:
If Sum = 10 Then _

If MsgBox(Sum has reached a value of _


& Sum & . & Chr(10) & _
Chr(10) & Yes to continue. & _
Chr(10) & No to stop., vbYesNo) = vbNo _
Then Exit Sub
End If
The MsgBox function can also be used to display meaningful information to users,
especially when a runtime error occurs, which is a topic to be discussed next: Error
handling.

Error handling
Without error handlers, any runtime error that occurs is fatal; that is, an error message is
displayed and execution stops abruptly. Rather than letting users helplessly facing error
messages, it is a good practice to trap errors and take appropriate actions, such as
displaying a more helpful message, in your error-handling code.
You can enable an error handler by using either the On Error Resume Next statement or
the On Error GoTo line statement. An error handler is enabled whenever the On Error
statement of the error handler is executed.
To disable error handling and let VBA handle errors, the On Error GoTo 0 statement is
executed. Table below briefly explains the role of these On Error statements:
On Error statement

Description

On Error Resume
Next

To ignore any runtime errors that occur and to continue with the next
statement. However, you might then use the Err object to determine
the next cause of action.

On Error GoTo line

If a runtime error occurs in a procedure, execution jumps to the line


of code labeled line in the procedure. The section of code marked by
line is called an error-handling routine. The routine must be in the
same procedure as the On Error statement.

On Error GoTo 0

Disables any error handling and let VBA handle errors.

To allow error handlers in your VBA code to work, make sure that you have enabled the
Break on Unhandled Errors option in the General tab of the VBE Options dialog box.
(Choose Tools | Options from the menu bar to display the Options dialog box.)
To understand the discussion on error handlers later in this section, execute the following
steps:
1. Enter the Sub procedures listed in this section into a VBA module.
2. Press F8 to step through the code in those procedures.
3. Observe the output in the Immediate window and follow the flow of control,
especially when runtime errors occur.

A sample procedure with no error handler


The following Sub procedure shows that without error handlers in a procedure, any
runtime error is fatal.
Sub Sample0()

Without any error handler

Debug.Print 1 / 0 fatal error


Debug.Print other code not executed
End Sub

Sample procedures with an error handler


If there is no error handler in a called procedure, when a runtime error occurs, control is
passed to the error handler in the calling procedure.
The following calling procedures Sample1a and Sample1b (with the error handlers On
Error Resume Next and On Error GoTo err1, respectively) and the called procedure
NoErrHandlerSub (without any error handler) show that when the runtime error divisionby-zero occurs in the called procedure, control is immediately passed to the calling
procedures.
Sub Sample1a()
Illustrating what happens when an error
occurs in a called procedure with
no error handler

On Error Resume Next


Debug.Print 1 / 0
Call NoErrHandlerSub
Debug.Print 1 executed
On Error GoTo 0 optional
End Sub

Sub Sample1b()
Illustrating what happens when an error
occurs in a called procedure with

no error handler

On Error GoTo err1


Call NoErrHandlerSub
Debug.Print other code not executed
Exit Sub
err1:

Debug.Print 1 in err1 executed


End Sub

Private Sub NoErrHandlerSub()

Debug.Print 1 / 0
Debug.Print other code not executed
End Sub
A general practice is to end an On Error Resume Next statement with an On Error GoTo 0
statement. Even without the On Error GoTo 0 statement, all error handlers are
automatically disabled when a procedure is exited.

Sample procedures with two error handlers


If there is more than one error handler in a procedure, only the last enabled error handler is
activated when an error occurs. An active error handler is the one currently in the process
of handling a particular error.

On-Error-Resume-Next and On-Error-GoTo-line statements
In the following Sample2a procedure, at first On Error Resume Next is enabled, but later it
is disabled after On Error GoTo 0 is executed. On Error GoTo err1 is then the only enabled
error handler and to be activated when the division-by-zero error occurs.
Sub Sample2a()

The last enabled error handler


is activated when an error occurs

On Error Resume Next


Debug.Print 1 / 0
Debug.Print 1 executed
On Error GoTo 0

On Error GoTo err1


Debug.Print 1 / 0
Debug.Print Other code not executed
Exit Sub
err1:

Debug.Print 1 in err1 executed


End Sub
In the following Sample2bFail procedure, On Error GoTo err1 does not properly trap the
fatal error division-by-zero because both error handlers have been disabled after the
execution of On Error GoTo 0. To allow On Error GoTo err1 to properly trap the fatal
error, simply restate On Error GoTo err1 after On Error GoTo 0, as shown in
Sample2bOK.
Sub Sample2bFail()
On Error Goto O disables all enabled
error handlers in a procedure

On Error GoTo err1


Other code (without errors) goes here
On Error Resume Next

Debug.Print 1 / 0
Debug.Print 1 executed
On Error GoTo 0
Debug.Print 1 / 0 fatal error
Exit Sub

err1:

Debug.Print 1 in err1 not executed


End Sub

Sub Sample2bOK()
Overcoming by re-enabling the error handler

On Error GoTo err1


Other code (without errors) goes here

On Error Resume Next


Debug.Print 1 / 0
Debug.Print 1 executed
On Error GoTo 0
On Error GoTo err1 restate
Debug.Print 1 / 0
Exit Sub
err1:

Debug.Print 1 in err1 executed


End Sub

Two On-Error-GoTo-line statements
The following Sample2c procedure shows that the last enabled error handler, On Error
GoTo err2, is properly activated when the division-by-zero error occurs.
Sub Sample2c()
The last enabled error handler
is activated when an error occurs

On Error GoTo err1


Other code (without errors) goes here
On Error GoTo err2
Debug.Print 1 / 0
Debug.Print Other code not executed
Exit Sub

err1:

Debug.Print 1 in err1 not executed


Exit Sub
err2:

Debug.Print 1 in err2 executed


End Sub

Handling errors in error-handling routines

A fatal error in an error-handling routine


When an error handler of On Error GoTo line in a particular procedure is activated (that is,
in the process of handling an error in its error-handling routine), any On Error statements
of error handlers encountered in the routine are executed and hence enabled, but they
cannot be activated even when another error occurs. In other words, when an On-ErrorGoTo-line error handler is active, any runtime error in the error-handling routine is fatal.
See the Sample3Fail procedure below.
Sub Sample3Fail()
Any error that occurs in an errorhandling routine is fatal

On Error GoTo err1


Debug.Print 1 / 0
Exit Sub not executed

err1:

Debug.Print 1 in err1 executed


On Error GoTo err2 not working
On Error Resume Next not working
Debug.Print 1 / 0 fatal error
Exit Sub not executed

err2:

Debug.Print 1 in err2 not executed


End Sub

Overcoming fatal errors with the Resume statement
An active error handler in a procedure can be deactivated (but it still remains enabled) by
a Resume statement. This statement can only be used in an error-handling routine and an

error must have been occurred. Otherwise, an execution of the statement generates a
runtime error with an error message of Resume without error.
When a Resume statement is executed, VBE resumes execution at certain line of code in
the procedure, at which it depends on one of the following forms of the Resume statement.
Statement

Description

Resume

Execution resumes at the same line of statement that caused the


error.

Resume Next

Execution resumes with the statement immediately following the


statement that caused the error.

Resume line

Execution resumes at the line of code labeled line in the procedure.

When a Resume statement is executed, the current error handler is deactivated (but not
disabled). If a runtime error then occurs, the last enabled handler will be activated.
In the Sample4aInfiniteLoop procedure below, when the division-by-zero error occurs,
execution jumps to the line of code labeled err1. When the Resume statement is executed,
it resumes execution at the division-by-zero statement. Hence, the division-by-zero error
occurs again, On Error Goto err1 is activated again, and execution jumps to the line
labeled err1. Hence, the Sub procedure loops forever. The discussion here is for the
understanding of the flow of control when using the Resume statement. Please consult the
VBA Help system (by searching the word Resume) for a working example on how the
statement can possibly be used to rectify the cause of an expected runtime error.
Sub Sample4aInfiniteLoop()
Resuming execution at the line of code
that caused an error (without rectifying
the cause in the error-handling routine)
forms an infinite loop

On Error GoTo err1


Debug.Print 1 / 0
Debug.Print other code not executed
Exit Sub

err1:

Debug.Print 1 in err1 executed


Resume
End Sub
The following Sample4b procedure replaces Resume with Resume Next to avoid an
infinite loop. When the division-by-zero error occurs, execution jumps to err1. When the
Resume Next statement is executed, it resumes execution with the statement immediately
following the division-by-zero statement.
Sub Sample4b()
Resuming execution with the statement
immediately following the statement
that caused the error

On Error GoTo err1


Debug.Print 1 / 0
Debug.Print other code executed
Exit Sub executed

err1:

Debug.Print 1 in err1 executed


Resume Next
End Sub
The third possible way of using the Resume statement is shown in the following Sample4c
and Sample4d procedures. In each of the procedures, it shows that how the active error
handler On Error GoTo err1 is deactivated by the Resume statement and control is
diverted to a specified line of code to enable a new error handler. The newly enabled error
handler is then the last enabled error handler. It is activated when an error occurs. Hence,
the fatal error in Sample3Fail can be avoided and handled gracefully.
Sub Sample4c()
Deactivating an active error handler and
diverting control to a specified

line to enable a new error handler

On Error GoTo err1


Debug.Print 1 / 0
Exit Sub not executed

err1:

Debug.Print 1 in err1 executed


Resume Continue
Exit Sub not executed

Continue:

On Error GoTo err2


Debug.Print 1 / 0
Exit Sub not executed

err2:

Debug.Print 1 in err2 executed


End Sub

Sub Sample4d()
Deactivating an active error handler and
diverting control to a specified
line to enable a new error handler

On Error GoTo err1

Debug.Print 1 / 0
Exit Sub not executed

err1:

Debug.Print 1 in err1 executed


Resume Continue
Exit Sub not executed

Continue:

On Error Resume Next


Debug.Print 1 / 0
Debug.Print 2 executed
On Error GoTo 0
End Sub

The following Sample4dAlternative procedure is an alternative to Sample4d. It also shows
that the last enabled error handler, On Error Resume Next, is properly activated when the
division-by-zero error occurs. Try to comment the On Error Resume Next statement and
step through the code to see what happens. It forms an infinite loop.
Sub Sample4dAlternative()
Deactivating an active error handler and
diverting control to a new line
to enable a new error handler

On Error GoTo err1


Debug.Print 1 / 0
Exit Sub not executed


err1:

Debug.Print 1 in err1 executed


On Error Resume Next
Resume Continue
Exit Sub not executed

Continue:

Debug.Print 1 / 0
Debug.Print 2 executed
End Sub

Overcoming fatal errors with a called Sub procedure
An alternative to handling errors gracefully in an error-handling routine, without using the
Resume statement, is to use a called procedure in the routine because any On Error
statement in a calling procedure will become inactive when another procedure is called.
The following SampleAsIn4c and SampleAsIn4d procedures show the alternatives to the
Sample4c and Sample4d procedures, respectively.
Sub SampleAsIn4c()
An alternative to Sample4c by using
a called procedure

On Error GoTo err1


Debug.Print 1 / 0
Exit Sub not executed

err1:

Debug.Print 1 in err1 executed

Call GotoErr2Sub
End Sub

Private Sub GotoErr2Sub()

On Error GoTo err2


Debug.Print 1 / 0
Exit Sub not executed

err2:

Debug.Print 1 in Err2 executed


End Sub

Sub SampleAsIn4d()
An alternative to Sample4d by using
a called procedure

On Error GoTo err1


Debug.Print 1 / 0
Exit Sub not executed

err1:

Debug.Print 1 in err1 executed


Call ResumeNextSub
Debug.Print 2 in err1 executed
End Sub

Private Sub ResumeNextSub()

On Error Resume Next


Debug.Print 1 / 0
Debug.Print 2 executed
On Error GoTo 0
End Sub

Chapter 4: Some other tips on VBA programming


Other than the tools discussed in Chapter 1 and some tips discussed along the way in
previous chapters, below are some other tips on VBA programming for beginners.

Writing more efficient VBA code


Working with arrays instead of a range of cells
VBA and Excel are two different entities. VBA code that repeatedly switches between the
two will greatly increase the execution time. A better approach is to copy the entire range
once into an array, to work with that array, and to write back the result to the worksheet.
This will greatly reduce the number of times that the VBA code needs to switch between
these two entities.
Lets compare the execution times between these two approaches. Suppose you want to
count the number of numbers that are divisible by four in a range of 50,000 cells. The
code that works with an array is generally faster than the one switches between VBA and
Excel. The following table shows the execution times (in seconds) for two different
approaches in counting the number of numbers that are divisible by four in a range of
50,000 cells:
Working with a range of Working
cells
array
0.20

with

an

0.031

The listing below is an example of VBA code that works with a range of cells.
Sub WorkingWithRange()
To count the number of numbers that are
divisible by 4 in a range of 50,000 cells.
Dim r As Long, c As Long
Dim rN As Long, cN As Long
Dim cnt As Long
Dim StartTimer As Date, EndTimer As Date

StartTimer = Timer
rN = 500: cN = 100: cnt = 0
For r = 1 To rN
For c = 1 To cN
If Sheets(1).Cells(r, c) Mod 4 = 0 _

Then cnt = cnt + 1


Next c
Next r
EndTimer = Timer
Debug.Print cnt
Debug.Print EndTimer - StartTimer
End Sub
The next listing is an example of much faster VBA code that works with an array.
Sub WorkingWithAnArray()
To count the number of numbers that are
divisible by 4 in a range of 50,000 cells.
Dim x() As Variant
Dim r As Long, c As Long
Dim rN As Long, cN As Long
Dim cnt As Long
Dim StartTimer As Date, EndTimer As Date

StartTimer = Timer
rN = 500: cN = 100: cnt = 0
ReDim x(rN, cN)
Sheets(1).Activate
x = Range(A1).Resize(rN, cN)
For r = 1 To rN
For c = 1 To cN
If x(r, c) Mod 4 = 0 Then _

cnt = cnt + 1
Next c
Next r
EndTimer = Timer
Debug.Print cnt
Debug.Print EndTimer - StartTimer
End Sub

Disabling screen updating, alert displays, events, and


automatic calculations
If they are not necessary during the execution of your VBA code, disable them before the
execution and restore them to their initial settings right before the execution ends.
Restoring the settings rather than turning them all on is a good practice since some users
may have different settings, as shown in the following listing.
Declaration
Dim ScrnU As Boolean, DispA As Boolean, _

Evnt As Boolean, Calc As Long



Save the settings
With Application

ScrnU = .ScreenUpdating
DispA = .DisplayAlerts
Evnt = .EnableEvents
Calc = .Calculation
End With


Disable them before the execution begins
With Application

.ScreenUpdating = False
.DisplayAlerts = False
.EnableEvents = False
.Calculation = xlCalculationManual
End With


Here runs your code


Restore the initial settings
before the execution ends
With Application

.ScreenUpdating = ScrnU
.DisplayAlerts = DispA
.EnableEvents = Evnt
.Calculation = Calc
End With

Reducing the size of a working range


Reduce the size of a working range whenever possible.
Lets take a fictitious situation to discuss this. Suppose that you have written the following
SumSelectedRange1 procedure to sum up all numbers in a selected range of cells.

Sub SumSelectedRange1()
Sum numbers in a selected range

Dim cel As Range, sum As Double


If TypeName(Selection) <> Range Then
MsgBox Please select a range.
Exit Sub
End If
For Each cel In Selection
If VBA.IsNumeric(cel) Then _
sum = sum + cel
Next cel
MsgBox The sum of all numbers in & _
the selected range is & Sum
End Sub
If an entire sheet of cells is selected by a user, the above procedure will take a very long
time to complete its execution because it is actually processing 17,179,869,184 cells. The
above procedure can be improved by considering only used range of cells in the
worksheet, as shown in the following listing.
Sub SumSelectedRange2()
Sum numbers in a selected range
In case a user selected an entire row,
column, or sheet

Dim cel As Range, sum As Double


Dim WorkRng As Range
If TypeName(Selection) <> Range Then

MsgBox Please select a range.


Exit Sub
End If
Set WorkRng = Intersect(Selection, _
ActiveSheet.UsedRange)
For Each cel In WorkRng
If VBA.IsNumeric(cel) Then _
sum = sum + cel
Next cel
MsgBox The sum of all numbers in & _
the selected range is & Sum
End Sub
The ActiveSheet.UsedRange property returns the range of cells that are used in the active
worksheet. Hence, the SumSelectedRange2 procedure has greatly reduced the number of
cells to be processed.
However, the UsedRange property of the Worksheet object does not actually determine the
cells that have really been used. To understand what I meant, execute the following steps:
1. Start a blank workbook.
2. Enter something (a number or text) into cell A1 in a worksheet.
3. Press Ctrl+ (the down-arrow key) to reach the last cell in column A, that is, cell
A1048576.
4. Press Ctrl+ (the right-arrow key) to reach the rightmost bottom cell in the
worksheet, that is, cell XFD1048576.
5. Enter something (a number or text) into this cell XFD1048576.
6. Press Alt+F11 to activate VBE.
7. In the Immediate window, enter the following statement:
? ActiveSheet.UsedRange.Address
It returns $1:$1048576, that is, the address of the entire cells in the worksheet.

If the SumSelectedRange2 procedure is executed for a worksheet where cell A1 and cell
XFD1048576 are used, then there is no difference in term of execution between
SumSelectedRange1 and SumSelectedRange2 (if the entire worksheet selected by a user
before execution). Nevertheless, in very rare cases, only cell A1 (or any cells near cell A1)
and cell XFD1048576 (or any cells near the last column XFD and the last row 1048576)
are used, but in between these cells mostly unused.
Another way to improve the execution time of SumSelectedRange1 is to deal with only
those cells with numbers. VBA provides a quick way to identify those cells: the
SpecialCells method, as shown in the SumSelectedRange3 procedure below.
Sub SumSelectedRange3()
Sum numbers in a selected range
Only working with numbers

Dim cel As Range, sum As Double


Dim ConstantNumbers As Range, _
FormulaNumbers As Range
If TypeName(Selection) <> Range Then
MsgBox Please select a range.
Exit Sub
End If
On Error Resume Next if no cells were found
Set ConstantNumbers = Selection. _
SpecialCells(xlConstants, 1)
Set FormulaNumbers = Selection. _
SpecialCells(xlFormulas, 1)
On Error GoTo 0
Sum constant numbers
If Not ConstantNumbers Is Nothing Then

For Each cel In ConstantNumbers


sum = sum + cel
Next cel
End If
Sum numbers in formula cells
If Not FormulaNumbers Is Nothing Then
For Each cel In FormulaNumbers
sum = sum + cel
Next cel
End If
MsgBox The sum of all numbers in & _
the selected range is & Sum
End Sub

Commenting out a block of VBA code


1. In VBE, choose View | Toolbars | Edit to display the Edit toolbar. See Figure 1-1.
2. Select the lines of code that you want to comment out.
3. Hover the mouse pointer over the buttons on the Edit toolbar to identify the
Comment Block button and click on it.
To uncomment lines of comments, use the Uncomment Block button on the Edit toolbar.

Removing all comments in VBA modules in one click


1. In the code window of any module, choose Edit | Replace or press Ctrl+H to
display the Replace dialog box.
2. In the box labeled Find What, type the following:
*
3. In the box labeled Replace With, keep it empty.
4. In the Search frame, select the option whether you want all comments in the
current procedure, in the current module, or in the current project to be removed.
5. Tick only the check box labeled Use Pattern Matching.
6. Click the Replace All button.
Caution: If you use single apostrophe characters not only for comments, but also in
executable lines of code containing strings (for example, a statement with the string
Times up.), they will be removed too.

Getting a list of VBA built-in functions in the code window


Sometimes when you are writing a code statement, you need to type a particular VBA
built-in function, but you are not sure about the exact spelling. You may then use the VBA
Help system to check the spelling.
An alternative is to display a list of VBA functions and choose the right one in the list. To
accomplish this task, execute the following steps:
1. In the code window or the Immediate window, type VBA followed by a dot
character (.):
VBA.
A list of items is automatically displayed. If VBE does not display the list, enable
the Auto List Members option in the Editor tab of the VBE Options dialog box.
2. Use the up- and down-arrow keys and the Tab key to choose the right item in the
list.
Similarly, you can type the name of any object followed by a dot to get a list of members
of the object. For example, typing Range. displays a list of members of the Range object.

Dealing with the situation when Auto List Members does not
work
In certain circumstances, even you have enabled the Auto List Members option in the
Editor tab of the VBE Options dialog box, typing the name of an object followed by a dot
does not display a list of members of the object. The following code demonstrates one of
those circumstances:
Worksheets(1).
A list of members of the Worksheet object is not displayed after the dot.
Note: In the above code, Worksheets is a container (also known as a collection) object. It
contains a collection of objects of the same type. A container object is usually the plural
form of objects it contains. An object in the container can be accessed either by an index
or the name of the object.
To display a list of member of an object, define an object variable for that object and then
type the object variable followed by a dot.
The following code demonstrates the idea for the Worksheet object, and in general it can
be any object:
Sub WithAutoListDemo()
Dim ws as Worksheet

ws.
End Sub
A list of members of the Worksheet object is displayed after the dot.

Calling a private procedure from other module


Often you declare a procedure in a VBA module private, so that it can only be called by
procedures in that module. However, VBA provides a way to call a private procedure from
other module by using the Application.Run method.

Suppose the following two private procedures are stored in a standard VBA module.
Private Sub DemoSub()

Debug.Print Hello from DemoSub


End Sub

Private Function AddBoth( _

x As Long, y As Long) As Long


AddBoth = x + y
End Function

The two procedures can be called from other module with the following statements,
respectively:
Application.Run DemoSub
Application.Run(AddBoth, 2, 4)
To get immediate results of calling the procedures, you might type the following
statements into the Immediate window and press Enter after each:
Application.Run DemoSub
? Application.Run(AddBoth, 2, 4) returns 6

If the Sub procedure is stored in a non-standard VBA module, precede the procedure name
with the module name. For example, if the procedure is in the workbook module, enter the
following statement to call the procedure:
Application.Run ThisWorkbook.DemoSub

However, if the Function procedure is stored in a non-standard VBA module, preceding
the procedure name with the module name does not return a value, as shown in the

following example of statement in the Immediate window:


? Application.Run(ThisWorkbook.AddBoth, 2, 4)

Hiding public Sub procedures from the Marco dialog box


A public Sub procedure in a VBA module can be called by procedures in other module. If
you dont want the procedure to be shown in the Macro dialog box while keeping it to be
remained accessible by procedures in other module, you can add a dummy optional
argument to the procedure.
You might want to walk through the following example to get what I meant by executing
following steps:
1. Enter the following public procedure into a new or an existing standard or nonstandard VBA module:
Sub Main()
Debug.Print Hello from Main
Other code
End Sub
2. In VBE, choose Tools | Macros to display the Macro dialog box.
The Sub procedure named Main is there in the dialog box.
3. Modify the procedure by adding a dummy optional argument, as shown below:
Sub Main(Optional dummy As Byte)
Debug.Print Hello from Main
Other code
End Sub
4. In VBE, choose Tools | Macros to display the Macro dialog box again.
The Sub procedure named Main is no longer visible in the dialog box.
5. In the Immediate window, enter either of the following statements to call the
procedure:
Call Main
Application.Run Main
If the procedure is stored in a non-standard VBA module, precede the procedure
name with the module name. For example, if the procedure is in the workbook
module, enter either of the following statements to call the procedure:
Call ThisWorkbook.Main
Application.Run ThisWorkbook.Main

Some good programming practices


You may try to google the Internet for some programming practices. Among the practices
are:
Create several small procedures rather than one single large procedure: ideally one
procedure for one specific task.
Small procedures are clearer and easier to maintain, modify, and reuse.
Add a comment to describe what a particular block or line of code does.
Without comments, you will spend much more time to figure out what it does if you
need to revisit the code months later.
Use the Tab key for consistent indentation.
It clarifies the structure of your code and makes the code easier to read.
Add blank lines between blocks or lines of code that do different tasks.
They make the code easier to read.

Potrebbero piacerti anche