100% found this document useful (2 votes)
655 views

Advanced Excel Notes

study guide

Uploaded by

sanu sayed
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (2 votes)
655 views

Advanced Excel Notes

study guide

Uploaded by

sanu sayed
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 668

Solutions

With Excel
A User’s CookBook









Harish Gopalkrishnan
Copyright © 2016 by Harishkumar Gopalkrishnan
All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording, or by any information storage or retrieval
system, without the prior written permission of the copyright owner.

Trademarks: Microsoft Excel, Word, PowerPoint and Outlook are registered trademarks of Microsoft
Corporation in the United States and/or other countries. All other trademarks are the property of their
respective owners. The Author is not associated with any product or vendor mentioned in this book.
MySQL is a registered trademark of MySQL AB A Company.

Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence of
a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark
owner, with no intention of infringement of the trademark.

LIMIT OF LIABILITY/DISCLAIMER OF WARRANTY: THE AUTHOR MAKES NO
REPRESENTATIONS
OR WARRANTIES WITH RESPECT TO THE ACCURACY OR COMPLETENESS OF THE
CONTENTS OF THIS WORK AND SPECIFICALLY DISCLAIM ALL WARRANTIES, INCLUDING
WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE. NO
WARRANTY MAY BE CREATED OR EXTENDED BY SALES OR PROMOTIONAL MATERIALS.
THE INFORMATION IN THIS BOOK IS DISTRIBUTED ON AN “AS IS” BASIS, WITHOUT
WARRANTY. ALTHOUGH EVERY PRECAUTION HAS BEEN TAKEN IN THE PREPARATION OF
THIS WORK, NEITHER THE AUTHOR(S) NOR THE PUBLISHER SHALL HAVE ANY LIABILITY
TO ANY PERSON OR ENTITY WITH RESPECT TO ANY LOSS OR DAMAGE CAUSED OR
ALLEGED TO BE CAUSED DIRECTLY OR INDIRECTLY BY THE INFORMATION CONTAINED IN
THIS WORK. THE ADVICE AND STRATEGIES CONTAINED HEREIN MAY NOT BE SUITABLE
FOR EVERY SITUATION. IF PROFESSIONAL ASSISTANCE IS REQUIRED IN A SPECIFIC FIELD,
THE SERVICES OF A COMPETENT PROFESSIONAL PERSON SHOULD BE SOUGHT. THE
AUTHOR SHALL NOT BE LIABLE FOR DAMAGES ARISING HEREFROM. THE FACT THAT AN
ORGANIZATION OR WEBSITE IS REFERRED TO IN THIS WORK AS A CITATION AND/OR A
POTENTIAL SOURCE OF FURTHER INFORMATION DOES NOT MEAN THAT THE AUTHOR
ENDORSES THE INFORMATION THE ORGANIZATION OR WEBSITE MAY PROVIDE OR
RECOMMENDATIONS IT MAY MAKE. FURTHER, READERS SHOULD BE AWARE THAT
INTERNET WEBSITES LISTED IN THIS WORK MAY HAVE CHANGED OR DISAPPEARED
BETWEEN WHEN THIS WORK WAS WRITTEN AND WHEN IT IS READ.



Table of contents

CHAPTER 1 GETTING STARTED
1.1 FOR AN ANALYST/MANAGER NOT A PROGRAMMER
1.2 NEVER WRITTEN A MACRO? –PLEASE SEE THE APPENDIX
1.3 FLEXIBILITY, MAINTAINABILITY, USER-FRIENDLINESS
1.4 NO SELECTION, ACTIVEWORKSHEETS OR ACTIVATE …
1.5 USING THIS BOOK – VIEWING CODE EXAMPLES
1.6 WHAT YOU SHOULD ALREADY KNOW
1.7 WHAT IS NOT COVERED HERE

Part I: Working with data in the workbook


CHAPTER 2 THE BASICS
2.1 IMPORTANT NOTES ON ORGANIZING DATA
2.2 SELECT, DELETE, INSERT ENTIRE ROW/COLUMN
2.2.1 Moving to the first non-blank column on the Right/Left
2.2.2 Moving to the first non-blank row at the Top/Bottom
2.2.3 Best way to get last row/column in a table of data
2.2.4 Working with all rows in the table with ‘for’ loop
2.2.5 Some more useful keystrokes
2.3 SPECIFYING DATES IN MACROS
CHAPTER 3 A WIDE RANGE OF POSSIBILITIES
3.1 CONSTRUCTING A RANGE
3.2 FINDING VALUES WITH VBA –FIND METHOD
3.3 REPLACING VALUES WITH VBA – REPLACE METHOD
3.4 GETTING RANGE FOR TABULAR DATA (WHEN THINGS ARE
FINE)
3.4.1 Top-Left Cell known, no blanks - CurrentRegion
3.4.2 Top-Left and Right Cells known, blanks present
3.5 PUTTING FORMULAS IN A WORKSHEET WITH VBA
3.5.1 Formulas referring to another workbook or worksheet
3.5.2 Applying a formula to an entire column
3.5.3 Convert formulas to values – PasteSpecial Method
3.6 USING VB ARRAYS WITH WORKSHEET RANGE
3.6.1 A word on Array bounds
CHAPTER 4 AUTOFILTER
4.1 APPLYING AUTOFILTER
4.1.1 Autofilter with keystrokes
4.1.2 Autofilter with VBA
4.1.3 Autofilter - the nitty gritty
4.1.4 Autofilter with VBA– Column positions known
4.2 WORKING WITH FILTERED CELLS – SPECIALCELLS
4.2.1 Put formula or value in one column at a time
4.2.2 Copy filtered rows to a temporary sheet
4.3 AUTOFILTER WITH VBA – COLUMN POSITIONS UNKNOWN
CHAPTER 5 PIVOTTABLES AND VBA
5.1 REFERRING TO PIVOTTABLES IN MACROS
5.1.1 Setting name of a PivotTable
5.1.2 Linking to a VBA PivotTable object
5.2 UPDATING BOUNDS OF A PIVOT TABLE IN VBA
5.3 READING DATA FROM A PIVOT TABLE WITH VBA
CHAPTER 6 SORTING DATA IN A RANGE OR TABLE
6.1 SORT OBJECT OF A WORKSHEET
6.2 SORTFIELDS COLLECTION
6.3 SORTING A RANGE – COLUMN POSITION UNKNOWN
CHAPTER 7 HANDY WORKSHEET FORMULAS AND FUNCTIONS
7.1 THE POWER OF ARRAY FORMULAS…
7.2 …AND WHY WE USE THEM ONLY SPARINGLY
7.3 INDEX
7.4 MATCH
7.5 COUNTA
7.6 INDIRECT
7.7 TEXT FUNCTIONS – TRIM, CLEAN, TEXT
7.8 CONDITIONAL MATH - COUNTIFS, SUMIFS, AVERAGEIFS
Scenario A - Criteria for numeric values
Scenario B - Criteria for text values
7.8.1 Precautions and some useful points
7.9 OFFSET
7.10 ADDRESS
7.11 GETTING VALUES FROM TABLES (INDEX + MATCH)
7.11.1 Sample data- piping engineering
7.12 THE SAME THING DONE DIFFERENTLY (VLOOKUP +
MATCH)
7.13 A BETTER USE FOR INDEX + MATCH
CHAPTER 8 HANDY VISUAL BASIC FUNCTIONS
8.1 FUNCTIONS FOR ARRAYS
8.1.1 Length of & looping through arrays
8.1.2 Checking dimensions of an array
8.2 FUNCTIONS FOR STRINGS
8.3 FUNCTIONS FOR DATES
8.4 OTHER FUNCTIONS
CHAPTER 9 EXCEL TABLES
9.1 CREATING A TABLE ON THE WORKSHEET
9.2 USING TABLE AND COLUMN NAMES IN FORMULAS
9.2.1 In Mathematical functions
9.2.2 In Lookup functions
9.3 CREATING PIVOT TABLE WITH A TABLE AS DATA SOURCE
9.4 CREATING A CHART WITH A TABLE AS A DATA SOURCE
9.5 USING TABLE AND COLUMN NAMES IN VBA
CHAPTER 10 DYNAMIC NAMED RANGES
10.1 RULES FOR CHOOSING NAMES FOR A RANGE
10.2 SELF-EXPANDING NAMED RANGES ON WORKSHEET
10.2.1 Dynamic range with a single row
10.2.2 Dealing with Layout changes
10.2.3 Dealing with our expanding business
10.2.4 Dynamic range with a single column
10.2.5 Dynamic range for tabular data
10.3 DYNAMIC NAMED RANGES IN (ARRAY) FORMULAS
10.4 USING DYNAMIC RANGES IN CHART SERIES
10.4.1 Adding a new series:
10.5 CHOOSING ONE OUT OF MANY SERIES
10.6 CHARTS WITH N-PERIOD ROLLING RANGES
10.7 COUNTRY-STATE-CITY SELECTION (CELL VALIDATION
WITH DYNAMIC RANGES)
10.7.1 General method for cell validation with a named range
10.7.2 Preparing the reference data
10.7.3 Preparing the named ranges
10.8 USING NAMES IN VBA
CHAPTER 11 WORKING WITH CHARTS IN VBA
11.1 NAMING A CHART
11.2 OBJECT MODEL FOR WORKING WITH CHARTS
11.3 SAVING A CHART AS AN IMAGE FILE
CHAPTER 12 OTHER COOL STUFF
12.1 TRIGGER YOUR MACRO WHEN YOU ARE SLEEPING
12.1.1 Trigger macro in the same workbook
12.1.2 Trigger macros in multiple workbooks
12.1.3 Managing existing scheduled tasks
12.1.4 Editing the launcher workbook
12.2 ZIP AND UNZIP FILES WITH VBA
12.2.1 Creating Zip files
12.2.2 Unzipping files
12.3 UP(DOWN)LOADING FILES FROM WEB SERVERS WITH FTP

Part II: Bringing data in / Sending data out


CHAPTER 13 WORKING WITH EMAIL ATTACHMENTS
13.1 WORKING WITH MICROSOFT OUTLOOK
13.1.1 Coupling Outlook to Excel
13.1.2 The Outlook Object Model
13.1.3 Read emails and download attachments
13.1.4 Send emails with attachments
13.2 WORKING WITH LOTUS NOTES
13.2.1 Coupling Notes to Excel
13.2.2 The Lotus Notes Object Model
13.2.3 Code for downloading attached Excel files
13.2.4 Code for sending mails with attached Excel files
13.3 WORKING WITH GMAIL
13.3.1 Change Gmail settings to work with Outlook
13.3.2 Setup Outlook to work with Gmail
CHAPTER 14 MANAGING FILES ON DISK
14.1 MANAGE FILES WITH VBA - FILESYSTEMOBJECT
14.1.1 FileSystemObject
14.1.2 Folder
14.1.3 Folders
14.1.4 File
14.2 LET USER SELECT FILE(S) OR FOLDER(S) – FILEDIALOG
14.3 READING & WRITING TEXT FILES – TEXTSTREAM OBJECT
14.3.1 Ways to obtain a TextStreamObject
14.3.2 Properties and Methods of TextStream
14.3.3 Code to read a text file into Excel
14.3.4 Code to write Excel data to a text file
CHAPTER 15 WORKING WITH MS WORD
15.1 EASY AND STRAIGHTFORWARD – LINKING
15.2 AN ALTERNATIVE - MAIL MERGE
15.3 BIT DIFFICULT BUT FLEXIBLE – MACROS
15.3.1 Connecting Word to Excel
15.4 THE MS WORD OBJECT MODEL
15.4.1 Application
15.4.2 Using a running Word application – GetObject Function
15.4.3 Application properties and methods
15.4.4 Documents Collection
15.4.5 Document
15.4.6 Range
15.4.7 Bookmarks collection
15.4.8 Bookmark
15.5 CREATING A BOOKMARK MANUALLY
15.6 CREATING THE BASE FILE
15.7 UPDATING THE BASE FILE USING MACROS
15.7.1 Transfer text to a specific location
15.7.2 Transfer tabular data
15.7.3 Transfer a chart
15.8 TRANSFER A TABLE WITH VARYING NUMBER OF ROWS
CHAPTER 16 WORKING WITH MS POWERPOINT
16.1 CONNECTING POWERPOINT TO EXCEL
16.2 THE MS POWERPOINT OBJECT MODEL
16.2.1 Application
16.2.2 Presentations Collection
16.2.3 Presentation
16.2.4 Slides collection
16.2.5 Slide
16.2.6 SlideRange
16.2.7 Shape
16.2.8 Shapes collection
16.2.9 ShapeRange
16.2.10 Windows and ViewType
16.3 USING AN ALREADY OPEN PRESENTATION FILE
16.4 CREATING THE BASE FILE
16.5 UPDATING THE BASE FILE WITH MACROS
16.5.1 Transfer text to a specific location
16.5.2 Transfer data from an Excel Range
16.5.3 Transfer a chart
16.5.4 Transfer a table with varying number of rows
CHAPTER 17 MICROSOFT QUERY & DATABASES
17.1 SHORT VISIT TO THE DATABASE WORLD.
17.2 WORKING WITH MS QUERY
17.2.1 Launch MS query
17.2.2 Single table - The Query Wizard
17.2.3 Multiple tables – True power of MS query
17.2.4 MS Query interface
17.2.5 Selecting Data
17.2.6 Joining multiple tables
17.2.7 Setting Criteria
17.2.8 Closing MS Query
17.3 CREATING SUMMARIES IN MS QUERY
17.3.1 An example in Excel
17.3.2 Summary of imported data – Performance of sales force
17.3.3 Applying criteria to summarized rows
17.4 WORKING WITH IMPORTED DATA
17.4.1 Refreshing the data manually
17.4.2 Auto-Refresh at fixed intervals
17.4.3 Edit an existing query
17.4.4 Associating a formula
17.5 QUERY DATA AND VBA
17.5.1 Setup for VBA
17.5.2 Refreshing the data with VBA
17.5.3 Trigger a macro on data refresh
17.6 CONNECTING TO MS ACCESS
17.7 CONNECTING TO MYSQL (ODBC + WOOCOMMERCE)
17.7.1 Prepping up our database connection
17.7.2 Setting up ODBC driver and Data Source
17.8 CORPORATE DATABASES

Part III: Building a 3-Tier application in Excel


CHAPTER 18 SCENARIO AND USAGE OF THE 3-TIER
APPLICATION
18.1 INTRODUCTION
18.2 SCENARIO FOR DATA CAPTURE
18.3 SETTING UP THE DATABASE.
18.3.1 Setting up named ranges
18.3.2 Cells for holding data entered in forms
CHAPTER 19 CREATING FORMS ON A WORKSHEET
19.1 SETTING UP THE WORKSHEET
19.2 ADDING CONTROLS
19.3 SETTING PROPERTIES, CAPTION AND NAME OF CONTROLS
19.4 LINK TO CELLS, FORMULAS & MACROS
19.5 CONTROLS & MACROS (GENERAL FACTS)
19.5.1 Linking controls to macros
19.5.2 Events
19.5.3 Common properties
19.6 LISTBOX CONTROL
19.7 COMBOBOX
19.8 CHECKBOX
19.9 OPTIONBUTTON
19.9.1 Proper way of putting option buttons
19.10 BUTTON
19.11 SCROLLBAR
19.11.1 Fractional/Negative values & reversing the direction
19.12 SPIN BUTTON
19.12.1 Fractional/Negative values & reversing the direction
CHAPTER 20 CREATING VISUAL BASIC USERFORM
20.1 SETTING UP THE USERFORM & ADDING CONTROLS
20.2 SETTING NAMES AND PROPERTIES
20.3 FOCUS, TAB INDEX & TAB STOP
20.4 LINK TO MACROS
20.5 USERFORM
20.6 COMMON PROPERTIES OF CONTROLS
20.7 EVENTS FOR CONTROLS
20.7.1 Setting an Event
20.7.2 Common Events
20.8 LISTBOX
20.9 COMBOBOX
20.10 CAVEAT ABOUT SETTING ROWSOURCE
20.11 CHECKBOX
20.12 OPTIONBUTTON
20.13 COMMANDBUTTON
20.14 SPINBUTTON
20.15 SCROLLBAR
20.16 TEXTBOX
20.17 LABEL
CHAPTER 21 MIDDLEWARE – THE BRIDGE
21.1 CONVERTING FORM DATA TO DATABASE VALUES
21.1.1 When using a form on a worksheet
21.1.2 When using a VB UserForm
21.2 FORM VALIDATION
21.3 THE SWITCHBOARD FORM
21.4 CREATING OUTPUTS
21.4.1 Invoice
21.4.2 Top-5 components by volume
APPENDIX A ERROR HANDLING
A.1 SOME TERMINOLOGY
A.2 ON ERROR.... PLEASE DO SOMETHING
A.3 AVOIDING ERRORS
APPENDIX B VBA PRIMER, THE BARE ESSENTIALS
B.1 EXCEL VBA FUNDAMENTALS
B.2 EXCEL OBJECT MODEL
B.2.1 Application (use spreadsheet functions, freeze the screen &
more)
B.2.2 Workbooks Collection (open a new or existing file)
B.2.3 Workbook Object (get the path, save, get all the sheets& more)
B.2.4 Worksheets Collection
B.2.5 Worksheet Object
B.2.6 Range Object
B.2.7 PivotTable Object
B.3 EXCEL EVENTS
B.3.1 Accessing the events
APPENDIX C VISUAL BASIC FUNDAMENTALS
C.1 WRITING CODE - THE VISUAL BASIC EDITOR
C.2 LINE CONTINUATION AND COMMENTS
C.3 PRIMITIVE DATATYPES, VARIABLES, EXPRESSIONS AND
DECLARATION.
C.4 PROGRAMMING FOR THE REAL WORLD – OBJECTS,
METHODS, PROPERTIES
C.4.1 Declaring and Setting Objects
C.4.2 Releasing Objects
C.5 OPERATORS
C.6 CODE GROUPINGS
C.6.1 Modules
C.6.2 Sub-procedures
C.6.3 Function procedure
C.6.4 Share Subs, Functions & Variables across modules
C.7 MANY ITEMS OF THE SAME TYPE – ARRAYS &
COLLECTIONS
C.8 CHOOSING A PATH TO FOLLOW – DECISION STRUCTURES
C.8.1 If-Then-Else
C.8.2 Select Case
C.9 DOING SOMETHING AGAIN AND AGAIN – LOOPS
C.9.1 For Loop
C.9.2 While Loop
C.9.3 For Each Loop
C.10 MULTIPLE OPERATIONS ON THE SAME OBJECT – WITH
BLOCK
C.11 MISCELLANEOUS CONSTANTS





Chapter 1
Getting Started
Microsoft Excel has become one of the most widely used software program in
the world today. Initially it was thought that Excel is popular only because it is
designed to work with the Windows operating system. However, because of its
ease fo use, ability to deal with a wide variety of data sources and features for
automation, Excel has made a mark of its own and is now used at various levels
ranging from a simple To Do list to mid level engineering calculations to
complex financial models.
Our focus in this book will be to learn ways to use, in fact automate, Excel in
better ways to save the time and effort we spend on our routine tasks. Emphasis
will be on the business reporting tasks but the techniques described are general
and can be used for low to mid level analysis as well.
The primary focus will be on Excel, but we will also learn ways to make Excel
work with a wide variety of applications to accomplish different tasks.
1.1 For an Analyst/Manager not a Programmer
The aim of this book is not to make the reader an ace programmer. Instead, the
goal is to enable someone who spends a lot of time with spreadsheets (a typical
analyst, manager or business owner) to quickly put together a working solution
using formulas and macros.
If you are a seasoned programmer, you might find the treatment of programming
topics a little less rigorous. The focus will be on Excel and not on programming
techniques. This also means we will not delve deep into concepts of Object
Oriented Programming. But even in that case, we believe that this book will add
something to your repertoire of skills.
We assume that we and our team mates work in good faith and no one is
interested in developing malicious code or making unauthorized changes to
someone else’s code. All variables and procedures will be kept ‘Public’.
Another aim, is to cover a wide variety of fundamental concepts. Each topic
discussed in this book is vast enough to deserve a dedicated piece of literature.
However, this book will give the reader a good grasp on a large number of
fundamental concepts. So even though the solutions given here can be applied
directly, if need arises, the user can explore the topics further with ease.
1.2 Never written a macro? –Please see the Appendix
Please don’t be worried about all the code spread across the pages.
If you don't have any experience in writing macros, please don't panic. There are
two appendices provided to assist you.
In case you are completely new to programming, please see the Appendix C on
visual basic programming. It will introduce you to concepts like variables,
objects, loops and so on. After appendix C, read appendix B and then read the
main chapters.
If you have a bit of experience with object oriented programming and just want
to know more about programming in Excel, Appendix B would be a good place
to start.
In case you have experience writing macros, you can just continue reading
further.
1.3 Flexibility, Maintainability, User-friendliness
These are the goals we would strive to achieve whenever we are creating a
solution using Excel. Although no examples are provided in this section, we will
highlight the principles wherever they are applied in the book.

A) Flexibility

Bounds of Data:
We should be able to add/remove data in our worksheets as required, without
having to change a lot of formulas or macros.

Version Independence:
Flexibility also means that, as far as possible, we use features that are available
in most versions of Excel and not just the latest one. If we suspect that our
solution uses a feature of Excel that is version dependent, we must check for and
then provide a backup method that would work with older versions.
Data sources:
Our spredsheet should be ready to accept and process data from a reasonably
wide variety of sources.
This fact becomes much more important if we are in business with large
corporate customers and suppliers or, when we request data from some other
department within our own organization (for example, server and database
administrators in our company’s IT department). They would have their own big
IT systems that would generate automated reports in formats ranging from badly
formatted plain text files to well formatted XML documents. Of course we won’t
ask them to change their reporting software. Instead, we strive to adapt our code
and formulas to work with their input.

B) Maintainability
Whenever we add a new functionality, we should try and make use of whatever
functionality we have already built into our workbook.
This also means that when we create a component of a solution, like a summary
cell or a Named range (Chapter 10, Dynamic Named Ranges), we should try and
ensure that it is flexible enough to be used by any other feature that would come
up in the near future.

C) User-Friendliness
Common User Understanding
As far as possible we should use features that are commonly used and intuitively
understood, rather than things that may look esoteric but not well understood by
general users. For example:
Writing formulas usingcell addresses written as A1, B2 and so on; which is more
intuitively understood by users of Excel, rather than the R1C1, R2C2 style which
becomes slightly difficult to interpret.
Using commonly used features ensures that when we passon our solution to our
team mates who will use and maintain the spreadsheet, they will easily
understand the working of the solution and will be able to make small tweaks as
required.
Manageable File Size
When people are working on tight schedules, we cannot send an email to our
team mates with a bulky workbook with lots of sheets and formulas and ask
them to patiently download a file (usually, as it happens, just a few minutes
before a meeting) from their email or from a server and wait few more minutes
for it to open.
We also cannot ask users to change one single cell and expect them to wait till
all the big formulas get calculated.
For this reason, it’s always prudent to keep our data and processing logic
(macros and most of our formulas) in one file, compile the reporting sheets and
dashboards (with minimal dependence on the raw data) and send only those
sheets to our team members.
1.4 No Selection, ActiveWorksheets or Activate …
Usually, when we record a macro, the resulting code would contain words like
those listed below:
1. Selection: representing the cells we select when the macro is getting
recorded.
2. Activesheet: The worksheet currently visible on the screen.
3. ActiveWorkbook: The workbook currently visible on the screen.
And quite a few more…..
However, in this book, our aim is to develop methods to work with multiple
workbooks with a single macro transferring data from one workbook to another.
As such, we will not be worried about which workbook is visible on the screen.
Hence we will not be using the objects like ActiveWorkbook or ActiveSheet.
Our macro won’t even be selecting any cells, instead the cells will be directly
controlled from the program irrespective of which cell the user may have
selected before running our macro.
1.5 Using this book – viewing code examples
This book is written in a way that allows the reader jump directly to the section
of interest. However, many of the sections build on concepts in earlier sections.
If you are starting out, it’s better to read chapter 3 first that deals with
Rangeconstruction.

· Code is written in Courier New font and Comments are written in Times New
Roman font.

· However, these may not stand out on all ebook readers.


· Large code blocks are enclosed in a box to make them stand out from
surrounding text
· The comment lines begin with a ( '//** ) and end with a with a ( **// ). This is
done to especially differentiate them from code lines. The standard Visual
Basic comment line just starts with an apostrophe ( ‘ )

· Whenever code continues to the next line the previous line ends with an
underscore ( _ ). This is standard Visual Basic line continuation. While
writing code readers can write the entire code in a single line without ( _ ).

· On some ebook readers a long line of code and comments may overflow to
the next line. In such cases, the reader may have to either decrease the font
size or turn the device in landscape mode to have better readability.

· Same is the case with slightly broad tables. Please change device orientation
to landscape for better readability.

Best effort has been made to keep all the code on a single page but this was not
possible in all cases and many examples have code split across multiple pages.
Please bear with this when reading the code.
1.6 What you should already know
This book is not written for someone completely new to Excel. Readers are
expected to know the basics like feeding data, copying, pasting, basic
formatting, creating charts and pivot tables and inserting simple formulas.
If you are completely new to Excel, you might want to read some of the basic
literature or consult with your friends and co-workers. Alternatively, a large
amount of literature is easily available that would teach the most basic
operations of Excel.
1.7 What is not covered here
We will not cover ways to format data.
Why? Because formatting usually becomes important in the presentation stage.
This is when the summarized results of processing a large amount of data are to
be presented to senior managers or a larger audience.
Note the following points which are usually true in normal course of work:
1. The boundary of the presentation is fixed. This means we always present a
fixed number of cells (usually shown as a table) or a fixed size of chart.

2. The variables to be reported in the chart or group of cell are fixed. Only the
values of the variables need to be worked out based on data processing.
For example, we might have a chart that shows sales numbers of different
products of our company. We made a report last month and we will make a
similar report this month.
The products and regions don’t change. It’s only the values of the sales figures
that get updated. Major change to our report would occur only if a new region or
product gets added.
In line with these observations, we can always manually preformat a group of
cells or a chart before doing any automation. Then, we can create macros or
advanced formulas to update the values in those cells using the methods we will
learn in this book. The related chart or PivotTable will get updated
automatically.

Chapter 2
The Basics
2.1 Important notes on organizing data
For proper functioning of any dashboard or report or macro, the input data has to
be structured in a certain way. That means:
1. Certain columns should be at particular location all the time so that in the
macros we can code in their positions with numeric values.
2. The data in a given column has to be of the same type (all text, or all
numbers or all dates) and should not contain certain characters like extra
spaces, special symbols or error values (#N/A, #Ref, #Value).
3. There should not be any row that is blank in all the columns.
Listed below are some important steps we should take to organize our data
before we start any type of processing. Although these seem to be based on
common sense they are often neglected and lead to malfunctioning of macros
and formulas.
1. Data should always be organized in tabular form (rows and columns).
2. First row of the table should always have column headers.
3. Whenever possible, first row of the table should be the first row of the
worksheet. Avoid empty rows on top that show company logo or
department/author name. These can be included in a separate sheet that
displays summaries.
4. At least one column should have values in all rows. Ideally this should also be
the first column in the table. Remove any row that is completely blank.
5. The top (header) row should not haveblank cells in any column.
6. Whenever possible, the first column of any table should be the first column of
the worksheet. Avoid empty cells in any rowin the leftmost column.
7. Summaries should be created on a separate sheet as far as possible.
2.2 Select, Delete, Insert Entire Row/Column
For all the code in this section, we create a workbook object and set it to
“Book1.xlsx’. If the macro is in the same work book that is being edited, then we
can use the ‘Thisworkbook’ object.Also, ‘r’ and ‘c’ refer to row and column
Numbers, of a given cell.
Example, Cell B5 has r = 5, c = 2
Dim wbk as Workbook
Set wbk = Workbooks(“Book1.xlsx”)






Select Entire Row/Column
Selecting entire row
Keystrokes: VBA:
Select any cell in the row
Shift + Spacebar Wbk.Worksheets(“Sheet1”).Rows(r).Select

Selecting entire column
Keystrokes: VBA:
Select any cell in the
column
Ctrl + Spacebar Wbk.Worksheets(“Sheet1”).Columns(c).Select


We can use the column letter if we want. For example, in our case for column B
we can use the code:
Wbk.Worksheets(“Sheet1”).Columns(“B”).Select

Delete Entire Row/Column (shift neighboring cells)
Deleting entire row (Data below the row, moves up one row)
Keystrokes: VBA:
Select any cell in the row
Shift + Spacebar
Ctrl + ‘-‘ Wbk.Worksheets(“Sheet1”).Rows(r).Delete


Deleting entire column(Data to the right of the column, moves to the left)
Keystrokes: VBA:
Select any cell in the
column
Ctrl + Spacebar
Ctrl + ‘-‘
Wbk.Worksheets(“Sheet1”).Columns(c).Delete


Delete only contents for Row/Columns
Clear entire row (Data below the row, moves up one row)
Keystrokes: VBA:
Select any cell in the row
Press: Shift + Spacebar
Press: ‘Delete’ Wbk.Worksheets(“Sheet1”).Rows(r).ClearContents


Clear entire column(Data to the right of the column, moves to the left)
Keystrokes: VBA:
Select any cell in the
column
Press: Shift + Spacebar Wbk.Worksheets(“Sheet1”).Columns(r).ClearContents
Press: ‘Delete’



Insert New Blank Row/Column
Inserting a row
Keystrokes: VBA:
Select the row where new
blank row is required
Press: Ctrl + ‘+’ Wbk.Worksheets(“Sheet1”).Rows(r).Insert

Inserting a column
(New column is inserted in place of the selected column. Existing data moves to
the right)
Keystrokes: VBA:
Select the column where
new blank row is required
Press: Ctrl + ‘+’ Wbk.Worksheets(“Sheet1”).Columns(c).Insert



Insert Copied Row/Column
Insert Copied Row
Keystrokes: VBA:
Select row to be copied
Press: Ctrl + ‘c’ Worksheets("sheet2").Rows(r).Copy
Worksheets("sheet2").Rows(r1).Insert
Select row where copied Application.CutCopyMode = False
row has to be inserted
Press: Ctrl + ‘+’
Press: Escape

Insert Copied Column
Keystrokes: VBA:
Select row to be copied
Press: Ctrl + ‘c’ Worksheets("sheet2").Columns(c).Copy
Worksheets("sheet2").Columns(c1).Insert
Select column where copied row Application.CutCopyMode = False
has to be inserted
Press: Ctrl + ‘+’
Press: Escape

2.2.1 Moving to the first non-blank column on the


Right/Left
Right-most column
Keystrokes: VBA:
Press: Ctrl + ‘Right Wbk.worksheets(“sheet1”).Cells(r,c). _ End(xlToRight)
Arrow’





Left-most column
Keystrokes: VBA:
Press: Ctrl + ‘Left Wbk.worksheets(“sheet1”).Cells(r,c). _ End(xlToLeft)
Arrow’


Note: to get the number of the right most column use (intCol is an integer
variable)
intCol = Wbk.worksheets(“sheet1”).Cells(r,c).End(xlToLeft).Column

2.2.2 Moving to the first non-blank row at the


Top/Bottom
Bottom-most row
Keystrokes: VBA:
Press: Ctrl + ‘Down Wbk.worksheets(“sheet1”). _
Cells(r,c).End(xlDown)
Arrow’


Top-most row
Keystrokes: VBA:
Press: Ctrl + ‘Up Wbk.worksheets(“sheet1”). -
Cells(r,c).End(xlUp)
Arrow’


Note: to get the number of the right most column use
lngRow = Wbk.worksheets(“sheet1”).Cells(r,c).End(xlUp).Row

Where lngRowis a Long Integer variable. Long integer is required because


newer versions of excel have over a million rows that may not fit into the integer
variable. This especially happens when combining data from several workbooks.
Caution:
The methods described in sections 2.2.1 and 2.2.2 provide only the first non-
blank row/column. We can’t rely on them to get the last row or column of our
entire table of data.
If our data is organized in a tabular form as described in Section 1.1, there is a
much better and faster way to get the bounds of your data.

2.2.3 Best way to get last row/column in a table of


data
Problem:
Users/Input providers have inserted blank rows and columns in their worksheets
for convenience or formatting or doing custom calculations. We need to work
with or compile the input files.

Solution:
Keystroke: We keep using keystrokes in section 2.2.1 and 2.2.2 till we reach the
last row or column.
Assumptions:
Data is in tabular form.
Row ‘r’ is the header row, Column ‘c’ is a key column
Last column has some data in row ‘r’. Last row has some data in column ‘c’

VBA:
‘//** The worksheet **//
Set wks = Workbooks(“Book1.xlsx”).worksheets(“Sheet1”)

‘//** 1. Go to the last column of the Worksheet in row r **//
‘//** 2. Then go to the first nonblank column **//
intCol = wks.Cells(r, wks.Columns.Count).end(xlToLeft).Column

‘//** 1. Go to the last row of the Worksheet in row c **//
‘//** 2. Then go to the first non-blank row at the top **//
lngRow = wks.Cells(wks.Rows.Count, c).end(xlUp).Row

Where lngRow is a long-integer variable and intCol can be an integer variable.

2.2.4 Working with all rows in the table with ‘for’


loop
We use the value of ‘lngRow’ variable we derived in the previous section.
For r = 2 to lngRow step 1
‘//** Value in Column 5 in Row r**//
Wks.Cells(r,5).Value
.
.
Next r

2.2.5 Some more useful keystrokes


Action Keystroke
Convert formulas in a column to values 1. Select Full column
2. Ctrl + ‘c’
3. Go to top most cell in the
same column
4. Press ‘Alt’ + ‘e’
5. One by one, press ‘s’, ‘u’,
‘enter’
Copy value in current cell from the cell Ctrl + ‘d’
above
Copy value in current cell from the cell Ctrl + ‘r’
on the left
2.3 Specifying dates in macros
Specifying dates in Excel macros is slightly tricky. This is especially true when
we try to specify date as a string. We would see an example of this when we are
working with emails and want to find emails received on or after a particular
date.
It is OK when we are dealing with dates that are not ambiguous like 31/03/2015.
We know that this is the same as 03/31/2015 since there are only 12 months. But
what about 01/10/2015 and 10/01/2015.
What if we have a conditional check like this:
If (invoicedate > “10/01/2015”) Then . . .

If we are not careful, our macro may give erroneous results. This happens
especially when we try to specify a date as string and our macro has to process
that input. Examples of such situations would be:
1. When we try to find a particular date in a range of cells.
2. When we write a macro to process emails received after a particular date.
3. When we want to find or filter rows on a worksheet based on dates.
4. We share our workbook with team members working in a different country
where different date format is followed for example dd/mm/yy instead of
mm/dd/yy.
Along with the issue of month and day ambiguity, there is also the issue of date
separators.
In such situations, Excel (on the worksheets) resolves the date based on the short
date format specified in the regional settings also known as the System Locale.In
Windows, these can be found in “Control panel -> Clock, Language, Region” -
>“Region”.
However, when dealing with dates in our macros, we can run into problems.
There are two situations when care has to be taken when specifying dates:
A) When we are dealing only with dates
To avoid any problems, it is always better to use the built in functions of Visual
Basic when just dates have to be specified. The functions are:
Date: This function provides us the current date in the appropriate format. The
function returns a value of Date datatype. Based on this value we can calculate
past dates. So now if we want to process only those emails that were received
within the past 5 days we would have some code like
If(receivedDate >= (Date - 5)) Then ..

The variable ‘receivedDate’ is of Date datatype.


DateSerial: This function allows us to specify the day, month and year parts of
the required date separately and then internally uses the correct format. The
function returns a Date so the return value has to be assigned to a variable of
Date datatype. The syntax is
Dateserial(year,month,day)

Now, to find the row having the date 1- Oct-2015 in column B of Sheet1 the
code would be as follows
Worksheets(“sheet1”).Columns(2).Find(Dateserial(2015,10,1)).Row

In this case, column B of Sheet 1 has been formatted to hold dates.


B) When we try to combine dates with strings
This situation most often occurs when we want to apply an autofilter on a
column containing dates. The filter criteria (See Chapter 4 for details) have to be
specified as strings. The criteria would contain the comparison operators ( = , > ,
< and so on) combined with a string containing the date in appropriate format.
In this case we need to realize that our macro’s code depends on the Code
Locale, which can be different from the System Locale (mostly outside United
States). In VBA’s code locale, dates are always expressed in “mm/dd/yyyy”
format.
Hence whenever we are using strings to specify dates that will be further
processed by our macro, we need to use the “mm/dd/yyyy” format.
For specific examples, please refer to code examples in sections 4.1.4 and
section 21.4.2.
Chapter 3
A wide Range of possibilities
A Range is the most fundamental object when creating a macro to operate on
data present on a worksheet. Before our macro can perform an operation, we
need to specify the group of cells, the Range, on which that operation has to be
performed.
In this chapter we will see different ways in which our macro can get a reference
to a Range on a worksheet. Towards the end of the chapter we will take a look at
few common operations that we can perform once we have a Range.
3.1 Constructing a Range
Problem:
When our macro operates on a table with variable number of rows, we need to
create a range object that refers to a rectangular area on a spreadsheet. We can
then perform operations on this range like copying, doing calculations and so on.
Assumption:
1. Data is in tabular form.
2. We know the top left cell where the table begins. For the code example below,
we assume it is “A1”. If not known, find the top left cell using methods
described in Chapter 2 sections 2.2.1 and section 2.2.2.
3. Column ‘c’ is the key column. It has data in all the rows of the table.
Solution:
Recall that, as mentioned in Appendix B, a range object can be created in two
ways
1. Use the top left and bottom right cells to create entire range.
2. Use the address of the entire rectangular region.
Using the top left and bottom right cells

‘//** Range object not initialized **//
Dim rng As Range

‘//** Long Integer for last row number **//
Dim lngRow As Long

‘//** Integer for last column number **//
Dim intCol As Integer

‘//** Integer for top most row number **//
Dim intTop As integer

‘//** Integer for left most column number **//
Dim intLeft As Integer

‘//** Set the top-left cell of the range, assumed to be A1 for this example. **//
‘//** We can use the values directly in the code without declaring the variables. **//
intTop = 1
intLeft = 1

‘//** Get the last row and last column of the table **//
lngRow = wks.Cells(wks.Rows.Count, c).End(xlUp).Row
intCol = wks.Cells(1,wks.Columns.Count).End(xlToLeft).Column

‘//** Construct the range by setting the top left and bottom right cells **//
Set rng = wks.Range(.cells(intTop,intLeft),.cells(lngRow,intCol))


Using the address of the entire rectangular region

‘//** String for holding Range address **//
Dim strAddr as String

With wbk.wks
‘//** Address of top left cell **//
strAddr = .cells(intTop,intLeft).Address

‘//** Concatenate the address separator **//
straddr = strAddr & “:”

‘//** Address of bottom-right cell **//
straddr = strAddr & .cells(lngRow,intCol).Address

‘//** Create the range **//
Set rng = .Range(strAddr)
End with

3.2 Finding Values with VBA –Find method
In this section we will see how how we can use a macro to find a value in a
Range of cells. Though not directly related to getting a reference to a Range, this
technique is useful when the Range that we want to create later on depends on
location of cells containing specific values.
Problem:
We need to operate on rows where a particular column has certain value(s). We
need to find the values in that column first to know the relevant row.
Solution:
In the concerned column, find the cells having the value(s) we are looking for.
Then work with the row/cell.
Keystroke (when working on spreadsheet):
· Ctrl + Spacebar to select the column Or, Shift + Spacebar to select the
row
· Ctrl + ‘f’
· In the box that opens, type the value to be found
· ‘Enter’
· Work with the found value.
· Repeat till all values are found
VBA:
Use the Find and FindNext methods of a range object.In the code example
below, we will do as follows:
1. Take a value from one open workbook (Sheet1 of Book1.xlsx)
2. Search for that value in a column in another open workbook(Sheet2 of
Book2.xlsx)
3. If the value is found, we change the contents of that cell and find next one.
4. We repeat till no more values are found.
The value to be searched is a string, but we can use any of the primitive
datatypes.
We have searched for the value in the entire column E of Sheet2, but we can
construct a limited range like “A2:A500”. That will save a little bit of work for
Excel and even speed up the macro slightly. Here is the full code.Note that
instead of a column, we can also find values in a row. Just set rngSource variable to
a row in the spreadsheet.

Sub FindAndWork()

‘//** The range of cells in which we want to find the value **//
Dim rngSource as Range

‘//** The range (single cell) where the value is found **//
Dim rngFound as Range

‘//** A string variable that will hold the value to be found **//
Dim strVal as String

‘//** A binary variable to decide when to stop searching **//
Dim bKeepLooking as Boolean

‘//**Set the range where the value is to be found. In this case it is column E on Sheet2 **//
Set rngSource = Workbooks(“Book2.xlsx”). _ worksheets(“Sheet2”).Columns(5)

‘//** Read the value to be found **//
strVal = Workbooks(“Book1.xlsx”). _
Worksheets(“Sheet1”).Cells(5,2).Value

‘//** Remove extra white spaces with Trim **//
strVal = Trim(strVal)

Set rngFound = rngSource.Find(strValue, Lookin:=xlValues)

‘//** Continue searching but only if atleast one value is found **//
If Not (rngFound Is Nothing) Then

‘//** One value found, keep looking for more **//
bKeepLooking = True

‘//** Save the address of the first cell that was found to avoid repeat search **//
firstAddress = rngFound.Address

While(bKeepLooking)
‘//**----------------------------**//
‘// ** Work with the found value. Here we just change the value we found to a fixed string
**//
rngFound.Value = “I changed this cell”
‘//**----------------------------**//
‘//** Find the next value **//
Set rngFound = rngSource.FindNext(rngFound)

‘//** If no more values are found OR **//
‘//** If program control ends up at the first cell that was found **//
‘//** Stop looking further **//
If (rngFound Is Nothing) Or _
(rngFound.Address = firstAddress) Then
bKeepLooking = False
Endif
Wend
End If
End Sub

3.3 Replacing values with VBA – Replace method
Problem:
We need to do work on rows where particular column has a certain value. The
values have to be replaced with some other values
Solution:
Keystroke:
· Ctrl + spacebar to select the column, Shift + Spacebar to select the row
· Ctrl + ‘h’
· In the box that comes up, type the value to be found and the replacement
value
· ‘Enter’ to replace one value
· To replace all values, click on ‘Replace All’
VBA:
Use the Replace method of the range object. See chapter 1 for details.
In the following code example, we replace error values ‘#N/A’ in column ‘F’ of a
workbook with the words ‘Not Available’.
Sub ReplaceValues()

‘//** The range in which we want to find the value **/
Dim rngSource as Range

‘//** The range (single cell) where the value is found **//
Dim rngFound as Range

‘//** Set the range where the value is to be found **//
Set rngSource = _ Workbooks(“Book2.xlsx”).Worksheets(“Sheet2”).Columns(F)

‘//** Replace every occurrence of ”#N/A” with ”Not Available” **//
rngSource.Replace What:=”#N/A”, Replacement:=”Not Available”
End sub

3.4 Getting range for tabular data (when things are
fine)
We will now look at ways to get the range of a data arranged as rectangular
table. Please note that the data has not been marked as an Excel Table (using
‘Insert’-‘Table’) commands.
In the examples that follow, we will create reference to the entire range of data
into a Range object named rngData. Please note that these are hypothetical
examples. The point highlighted is that when we receive data for processing, we
should look for factors that help us to establish the boundary of the region
containing the data.

3.4.1 Top-Left Cell known, no blanks -


CurrentRegion
Usually when co-workers send us input files, the data is presented in some fixed
format. In these cases:
1. We know the top-left cell of the data
2. There is no row or column that is completely blank in the entire table of data.
Meaning, that every row has data in atleast one column and, any blank column
has atleast the column header that is non blank.
In this situation we can get the entire Range of the data using a single line of
code.
Suppose the top left cell is “C5”, in worksheet “InputData”, in a workbook that
is set to the workbook object ‘wbk’
Set rngData = wbk.worksheets(“InputData”).Range(“C5”).CurrentRegion

3.4.2 Top-Left and Right Cells known, blanks


present
Consider a situation where we know the top left and top right corners of the data.
But, there is a possibility of having at least one blank row or column
Such a table is shown in the figure 3.4.2. The region B6:C11”is the range we
would get if we use the ‘CurrentRegion’ property of cell B6.
We know that the table always starts at cell B6 and the top right cell is G6. We
also know that there is no other data below the table.
This table does not have a key column, meaning we cannot say that a particular
column will always have data in all rows let alone the last one. Here is how we
proceed:
1. For each of the columns B to G, start at the last row of the worksheet
2. Use the End property and find the first non-blank row and note the row
number
3. Take the maximum value of row number that is found.
4. This row is the bottom row of the tabular data

Fig 3.4.2

We use the fact that the vertical bounds of the table are known (columns 2 to 7).
Notice the use of WorksheetFunction property.

Dim lngLastSheetRow As Long


Dim lngLastDataRow As Long

‘//** Initially assume that the last row of data is same as the first row **//
lngLastDataRow = 6
With wbk.Worksheets(“InputData”)
lngLastSheetRow = .Rows.Count
For c = 2 To 7 Step 1
‘//** Compare the last rows previously established to **//
‘//** last row of the current column **//
‘//** Take the larger value. **//
lngLastDataRow = _
Application.WorksheetFunction. _
Max(.Cells(lngLastSheetRow,c).End(xlUp).Row, _
lngLastDataRow)
Next c
Set rngData = .Range(.Range(“B6”),.Cells(lngLastDataRow,7))
End With



3.5 Putting formulas in a worksheet with VBA
In VBA, every cell object that represents a cell on a worksheet has a Formula
property.

The ‘Formula’ property is a string that can be written as the formula that we
enter into a cell on the worksheet.

We just set the ‘Formula’ property of the cell to the required string. For
example,suppose we run a macro with the following code

Thisworkbook.Worksheets(“sheet1”).Cells(5,2).Formula = “=Sum(A1:A10)”

When we go to Sheet1 we will find that cell B5, contains the sum of cells A1 to
A10 and clicking in cell B5 shows the formula “=Sum(A1:A10)” in the formula
bar.

Most of the times, we can just type the required formula in the formula bar, copy
it and paste it in the code of our macro. However, care needs to be taken when
the formula uses text strings, or when it refers to another worksheet or workbook

3.5.1 Formulas referring to another workbook or


worksheet
Problem:
Often we need to add formulas to cells that refer to cells in another workbook or
another sheet in the same workbook.
Such a need arises when our macro combines data from multiple Excel files and
data from one file has to be fetched into another file using formulas like
“VLOOKUP”. For example, consider the following two formulas:

1. =SUM('[KPI Dashboard - Version II.xlsx]Data'!$E$11:$E$88)

2. =COUNTA('C:\Input\[Issues v2.xlsx]Pending List'!$C$2:$C$25)

In the second formula, please note that the workbook “Issues v2.xlsx” may be
closed when we are inserting the formula in the current workbook. But in that
case we need to specify the full path of the workbook that the formula will refer
to.
Notice also that the names of workbooks and worksheets can contain blank
spaces and other characters permitted by the operating system. In such cases we
need to be extra careful when creating our formula string because the code can
become difficult to read and maintain.
Solution:
As mentioned earlier, the best way is to enter the formula manually in one cell
and copy its string from the formula bar and use that in our macro. But let us
now look at a way to handle this if we absolutely have to do this in our macro.

We create a formula in a separate string as follows: (Take special note of the
additional characters that are used)
1. Name of the workbook is placed between square brackets ([ ]). If the
workbook is closed at the time of entering the formula, we need to provide
the full path of the workbook.
The path of the workbook is separated from the name of the workbook with a
path separator (“\” in case of Windows).
2. Then follows the name of the worksheet.
3. Combination of 1 and 2 is then placed between single quotes ( ‘ ‘ ).
4. Then we add an exclamation mark ( ! ).
5. Then follows the address of the range on which we want to do the
calculation.

The string formed in steps 1 to 5 is then combined with equal to sign ( = ) and
the name of the function we want to apply and normal curved bracket ‘ ( ) ‘.

Let us now apply the steps to create the second formula in the problem
description. Assume that the file “Issues.xlsx” is currently closed.

Notice the following:
1. We have used the concatenation operator (&) quite extensively.
2. Many of the steps can be combined, like the path, name and square brackets
can be added in one single line of code. But these have been shown separately
to highlight the concepts.

Dim strFormula As String

‘//** Notice the path separator (\) at the end **//
strFormula = "C:\Input\"

‘//** Add the workbook name and square brackets **//
strFormula = strFormula & "[Issues V2.xlsx]"

‘//** Add the worksheet name **//
strFormula = strFormula & "Pending List"

‘//** Add the single quotes, exclamation mark and the range address **//
strFormula = "’" & strFormula & "’" & "!" & "$C2:$C25"

‘//** Combine with the worksheet function **//
strFormula = "=CountA("& strFormula &")"
‘//** Insert the formula in cell B5 of Summary sheet in current workbook. **//
ThisWorkbook.Worksheets("Summary").Range("B5").Formula = strFormula

3.5.2 Applying a formula to an entire column


Problem:
Before working with our input, we need to add a column that has values
calculated based on some other column.
Solution:
Apply a formula to first row in the column, copy the formula till the last row of
the table
Assumptions:
1. First and last rows of the table are known. If not, find these using one of the
methods described in Chapter 2.
2. The table structure is as shown in the following figure:


3. The Discount column will be added in column G and the Discount percentage
will be based on the type of SaleMedium. The table below shows the discount
criteria.

Discount Criteria Discount(%)
(SaleMedium)
Online 15
Direct 20
Retail 5

Formula in cell G2 will be:

=IF(B2="Online",15,IF(B2="Retail",5,IF(B2="Direct",20,0)))

The formula chosen shows:
1. How we can put nested functions(function within function) on aworksheet.
2. How the double quotes required in the formula are created using four quotes
(""""). This is a string which can be joined to the other parts of the formula to
form the complete string.

Here is the VBA code for inserting the formula in column G to all the rows of
the table. Notice how we have used the range of cells without using a range
object

Wks : is the worksheet object in which the formula has to be placed.
lLastRow : is the last row for the table of data.

'//** Put the formula in one cell in the second row-notice the four double quotes **//
wks.Range("G2").Formula =_
"=IF(B2=" & """" & "Online" & """" & _
",15,IF(B2=" & """" & "Retail" & """" & _
",5,IF(B2=" & """" & "Direct" & """" & ",20,0)))"
'//** Copy the formula **//
wks.Range("G2").Copy

'//** Using PasteSpecial method, apply the formula in all the rows of the table in column G **//
wks.Range("G2:G" &cstr(lLastRow)) _
.PasteSpecial(xlPasteFormulas)
‘//** Turn off the copy mode **//
Application .CutCopyMode=False

3.5.3 Convert formulas to values – PasteSpecial


Method
Problem:
We just added a column with calculated values. On inserting or moving some
other column, the formulas in the new column get disturbed.
Solution:
Copy the newly added column containing formulas, paste it on itself as values.
The code for the formula used in section 3.5.2 would be:
'//** Copy the formula **//
wks.Columns(“G”).Copy

'//** Use PasteSpecial to put the value of calculations **//
‘//** in all the rows of the table in column G/**//
wks.Range("G1”).PasteSpecial(xlPasteValues)

‘//** Turn off the copy mode **//
Application.CutCopyMode=False

3.6 Using VB Arrays with worksheet Range
It is possible to take the values of a range of cells from a worksheet into a Visual
Basic array. We can also transfer values from arrays to cells on a worksheet.
The advantage is that our macro would not have to go back and forth between
the worksheet and the code variables and this speeds up the execution of our
macro.
Following points should be kept in mind when creating an array from a
spreadsheet range:
1. The arrays are always of type ‘Variant’.
2. To create an array from a range, we use the ‘Value’ property of the Range
object.
3. Whether we use a 1-dimensional or a 2-dimensional range, we always get a 2
dimensional array.
4. If the range used is a column of cells, the upper bound of first dimension of
the resulting array is equal to the number of cells in the column, the bound of
second dimension (number of columns) is 1.
5. If the range used is a row of cells, the bound of first dimension of the resulting
array (the number of rows) is 1 and the upper bound of second dimension is
equal to the number of cells in the row.
Following should be remembered when transferring values from an array to cells
on a worksheet:
1. Value in first row and first column of array, goes into the top left cell of the
range.
2. First we need to create a range object having the right dimensions. This is
done by using the Resize method of a range object on a single cell range.
3. The single cell used becomes the top left cell of the resulting range.
4. If the array is a VB array (not created from a range, but created using the
Array() function of visual basic), it is always pasted as a row, by default. If
we want to paste it as a column, we need to use the Transpose function of the
Application object. Code example will make this clear.
The first code example in this section shows how arrays are created from range
of cells on a worksheet. Don’t let the names arr1DHor, arr1DVer, arr2Dconfuse
you. Remember, arrays created from spreadsheet cells are always 2 dimensional.
The names describe the type of range that is used for the array.
The code simply creates arrays and then prints out the values in the immediate
window. However, it does demonstrate important concepts of looping through
array members one by one.
The second example shows how an array can be transferred back to a worksheet.
We have made use of the LBound and UBound functions. In case you are not
familiar with these functions, please refer section 8.1 “Functions for arrays” in
chapter 8.
Code example – creating array from range on a worksheet

Sub Range2Array()
‘//** Variables to hold arrays created from worksheet ranges. **//
Dim arr1DHor, arr1DVer, arr2D As Variant

‘//** Array of integers , not created from range on a worksheet **//
Dim arrInt(5) as Integer

‘//** Initialize the array of integers within the code **//
arrInt = Array(1,2,3,4,5)

With ThisWorkbook.Worksheets("Sheet3")
‘//** 1 D array from one column of 10 cells **//
arr1DVer= .Range("D2:D11").Value
‘//** 1 D array from one row of 10 cells **//
arr1DHor = .Range("A1:J1").Value
‘//** 2 D array from 10 x 7 range of cells **//
arr2D = .Range("D2:J11").Value
End With
For i = LBound(arr1DVer, 1) To UBound(arr1DVer, 1) Step 1
‘//** Print the 1D vertical array **//
Debug.Print arr1DVer(i, 1)
Next i
For i = LBound(arr1DHor, 2) To UBound(arr1DHor, 2) Step 1
‘//** Print the 1D horizontal array **//
Debug.Print arr1DHor(1, i)
Next i
For i = LBound(arr2D, 1) To UBound(arr2D, 1) Step 1
For j = LBound(arr2D, 2) To UBound(arr2D, 2) Step 1
‘//** Print the 2D array **//
Debug.Print arr2D(i, j)
Next j
Next i
End Sub

Code example – transfer values from array to a range on a worksheet


Dim rngTgt as Range
‘//** Set a range object to point to the top left cell where **//
‘//** we want to paste the contents of array **//
Set rngTgt = ThisWorkbook.Worksheets("Sheet5").Range("A36")
‘//** Resize the range object to dimensions of the 1D vertical array **//
Set rngTgt = rngTgt.Resize(UBound(arr1DVer, 1), 1)
‘//** Set value of the new range to value of array **//
rngTgt.Value = arr1DVer
‘//** One line code for 1D horizontal array **//
ThisWorkbook.Worksheets("Sheet5").Range("A35"). _
Resize(1, UBound(arr1DHor, 2)).Value = arr1DHor

‘//** Set a range object to point to the top left cell **//
‘//** Where we want to paste the contents of array **//
Set rngTgt = ThisWorkbook.Worksheets("Sheet5").Range("A15")
‘//** Resize the range object to dimensions of the 2D array **//
Set rngTarget = rngTarget.Resize _
(UBound(arr2D, 1), UBound(arr2D, 2))

‘//** Set value of cells of range to values in arr2D **//
rngTgt.Value = arr2D
‘/** ----------- Code to transfer Visual Basic Arrays --------- **//
‘//** Transferring a Visual Basic Aarray not created from a spreadsheet range **//
Dim LenArr as Long
‘//** Calculate the length of the Visual Basic Array **//
LenArr = UBound(arrInt) - LBound(arrInt)+1
‘//** Set a range object to point to the first cell **//
‘//** where we want to paste the contents of VB array **//
Set rngTgt = ThisWorkbook.Worksheets("Sheet5").Range("A5")
‘//** Resize the range object to be a row **//
Set rngTarget = rngTarget.Resize(1,LenArr)
‘//** Set value of cells of range to values in Visual Basic Array **//
rngTgt.value = arrInt
Set rngTgt = ThisWorkbook.Worksheets("Sheet5").Range("A6")
‘//** Resize the range object to be a column **//
Set rngTarget = rngTarget.Resize(LenArr,1)
‘//** Set value of cells of range to values in Visual Basic Array **//
rngTgt.value = Application.Transpose(arrInt)

3.6.1 A word on Array bounds


Notice how we have avoided the inconvenience of starting index (worrying
whether it is 0 or 1) by simply using the LBound function on the array.
It should be noted that when we create an array from a worksheet range, the
lower bound of the array is always ‘1’. Hence in that case the length of the array
is equal to the upper bound of the array.
However, if the array is created using the Array() function of Visual Basic , the
lower bound would depend on the value set by a special statement, “Option
Base” statement, at the beginning of the every module.
In case an Option Base statement is not used, the default value for the lower
bound of an array in Visual Basic is ‘0’. In that case the length of the array
would be one more than the upper bound of the array (Upper Bound +1).
We should be able to know the length of an array so that we can use it in the
Resize method when pasting values in to worksheet cells as we have done in the
code examples in Section 3.6.
Chapter 4
Autofilter
In this chapter we will see how we can use VBA to filter data on a worksheet.
4.1 Applying Autofilter
Problem:
· We receive several input files from your team members each file having a
table of data.
· We have to create a compiled file, with data from all the files.
· From each file only those rows have to be taken that have specific values in
certain columns.
We have seen a method tofind and work with single rows of data. But, what if
we are dealing with thousands of rows of data and have to separate rows based
on different values in different columns?
Solution:
We use the Autofilter functionality provided by Excel.

4.1.1 Autofilter with keystrokes


Here are some tips to quicken your pace when working with Auto-filter
Ø Data needs to be arranged as a table
Ø No row is entirely blank.
Ø All columns have column headers.
Ø Go to top left cell of the table. If this is cell “A1”, press ctrl + Home to get
there quickly
Ø Press Ctrl + Shift + ‘8’ to select the entire table.
(If you are expecting blank rows in the table, Click the top left corner of the
worksheet – the red square shown in the Fig 4.1.1 to select the entire
worksheet.)

Fig 4.1.1
Ø Press Alt + ‘d’.
Ø Now press ‘f ‘ two times. And we have Auto filter applied to the entire table
Ø Now filter rows based on values in different columns and work with rows.
Ø Any time you want to ‘Show all data’ click Press Alt + ‘d’, press ‘f’, press ‘s’.
Ø To remove the autofilter, Press Alt + ‘d’, now two times press ‘f’
4.1.2 Autofilter with VBA
In macros, we can use the Autofilter method. Note that Autofilter is a method of
the Range object. So for applying Autofilter we first need a range. Once it is set,
this range can be obtained back with the Range property of the Autofilter object.
The required Range can be:
Ø A custom range constructed using one of the methods described
Ø Full worksheet, in which case, the VBA code would be
Set rng = wbk.Worksheets(“Sheet1”).Cells

wbk : is a workbook object that is already initialized.

Excel automatically selects the appropriate columns that bounds the table of
data.

4.1.3 Autofilter - the nitty gritty


Autofilter method applied without any inputs for the first time, applies the filter.
The filter drop-down arrows show up on the sheet.
Autofilter method should be applied again repeatedly with arguments/inputs for
each column based on whichwe want to filter the data. The arguments are as
follows:
1. Field: This is an integer. The column number whose values have to be filtered.
The leftmost column of the range is ‘1’. The column to the right of it is ‘2’ and
so on.

2. Criteria: For every column we can specify two criteria, related with an
operator. Criteria are specified as String variables. A table at the end of this
section gives a list of how each criteria can be specified in a macro’s code

3. Operator: How are the two criteria related? The permitted value is one of the
following
Ø xlAnd: Filter rows that satisfy both the Criteria simultaneously
Ø xlOR: Filter rows that satisfy at least one or, both of the Criteria

4.1.4 Autofilter with VBA– Column positions known


For ourCode Example we assume that:
1. We have received 3 workbooks from our team members.
“SalesInput_1.xlsx”, “SalesInput_2.xlsx”, “SalesInput_3.xlsx”,
2. The workbook containing our macro is in the folder Our workbook is in the
folder “C:\Sales\Reports\WorkingFiles”
3. We have kept the input files in a folder ‘Inputs’ which is created in the
“WorkingFiles” folder.
4. In each file, a table begins at Cell A1 of a worksheetnamed “SalesData”.
5. The table structure is as follows

6. The product ID column has entries of the form


ATMOB001, ATTAB001,ATLPT002,ATDTP002,
Where the 3rd, 4th and 5th letters indicate the type of product – Mobile Phones,
Tablets, Laptops and Desktops respectively

What if the column positions are different in each of the input files?Or, What
if the users have inserted extra columns in between?

We deal with that situation in the next section.
We want to make a report which requires a list of:
All Laptops or Tablets sold in second quarter of 2015, in the North and East
region where the SaleQty is between above 10 and up to 100
The criteria for filtering would be:
1. ProductID: Column 1, has either ‘TAB’ or ‘LTP’ in it
2. SellerRegion: Column 6, is either ‘North’ or ‘East’
3. SaleQty: Column 5, is above 10 and less than or equal100
4. SaleDate: Column 4, is between and includes 1-Apr-2015 and 30-June-
2015
The code for applying Autofilter looks like this:
Rng.AutoFilter Field:=1, Criteria1:="ctr1", _
Operator:=xlAnd, Criteria2:="ctr2"

Ø Rng : is the range on which filter is applied
Ø Field : is the position of the column relative to the first column in the range.
In our example, we need to repeat this code 4 times. Rng will be the columns A
to F.
Let’s see the full code for the macro. Notice how we have used the string
concatenation operator, ‘For’ loop and the ‘Open’ method of workbooks
collection.

Code Example – Autofilter in VBA – Column positions known
Sub FilterRows()

‘//** Declarations **//
Dim wbk as Workbook
Dim strFilePath as String
Dim rng, rngVisible as Range

‘//** string holding the path C:\Sales\Reports\WorkingFiles **//
strFilePath = Thisworkbook.Path

‘//**Partial Path of files **//
strFilePath = strFilePath & “\Temp\SalesInput_”

‘//** Work with one workbook at a time **//
For i = 1 to 3 step 1

‘//** Open the workbook SalesInput_i.xlsx **//
Set wbk = Workbooks.Open(“strFilePath” &i& “.xlsx”)

‘//** Set the range to refer to columns A to F on SalesData worksheet **//
Set rng = wbk.Worksheets(“SalesData”).Columns(“A:F”)

‘//** Filter for Product ID **//
rng.AutoFilterField:=1,Criteria1:="=*TAB*", _
Operator:= xlOr,Criteria2:="*LTP*"

‘//** Filter for SaleDate **//
rng.AutoFilter Field:=4, Criteria1:= ">=4/1/2015", _
Operator:= xlAnd, Criteria2:= "<=6/30/2015"

‘//** Filter for SaleQty **//
rng.AutoFilter Field:=5, Criteria1:= ">=10", _
Operator:= xlAnd, Criteria2:= "<=100”

‘//** Filter for SellerRegion **//
rng.AutoFilter Field:=6,Criteria1:= "North", _
Operator:= xlOr,Criteria2:= "East”
‘//**-------------------------------------------
‘//** Work with the rows here
‘//** Refer section 2.5 for code can be placed here
‘//** -------------------------------------------

‘//** Remove all filters **//
rng.Autofilter

‘//** Close the workbook (here we are not saving any changes) **//
‘//**and move to the next workbook **//
wbk.Close(False)
Next i
End Sub

4.2 Working with filtered cells – SpecialCells
Excel does not provide a straight forward way to work with filtered rows.
However, there are workarounds that would make our life easy. Let’s take a look
at few of the operations we can perform once we have filtered a set of rows.
We will see the use of the SpecialCells method of the Range object. For more
information, please see section B.2.6 in Appendix B.

4.2.1 Put formula or value in one column at a time


Suppose we want to work with the 2nd column of a Range (related to the range
object ‘rng’) on which we have applied a filter. Data is contained on a worksheet
named “SalesData”.
‘//** Create a range having only the visible cells of the second column **//
Set rngVisible =rng.Columns(2).SpecialCells(xlCellTypeVisible)
‘//** Save the value in the header row for column 2 in a variable **//
strHeader = rng.Cells(1,2).Value

It should be noted that the Cells property of a normal Range object cannot be
used on a filtered range (rngVisible). Meaning, we cannot access the second cell
in third row of the second column with the code

‘//** This does not work **//
rngVisible.Cells(3,2)

Work with each visible cell in the column:

For each c in rngVisible.cells
‘//** ……Work with the cell value or formatting…… **//
Nect c
‘//** Replace the header value **//
rng.Cells(1,2).value = strHeader

Put a value in one cell and copy it to the other cells:

rng.Cells(1,2).Value = “Not_Applicable”

wbk.Worksheets(“SalesData”).Select

rng.Cells(1,2).Copy

rngVisible.PasteSpecial(xlPasteValues)

‘//Turn off the copy mode.
Application.CutCopyMode = False

‘//Replace the header value
rng.Cells(1,2).value = strHeader

Put a formula in one cell and copy it to the other cells:
The following code puts a formula that refers to a column 2 columns to the right.
Notice the use of Offset and Address properties of the Range object.

Dim strForm As String

strForm = “=left(“ & rng.Offset(0,2).Address(False) & “,3)”

rng.Cells(1,2).Formula = strForm

wbk.Worksheets(“SalesData”).Select

rng.Cells(1,2).Copy

rngVisible.PasteSpecial(xlPasteFormulasAndNumberFormats)

Application.CutCopyMode = False

rng.Cells(1,2).value = strHeader

4.2.2 Copy filtered rows to a temporary sheet


In this method,
Ø Construct a rangeof all the filtered cells, using the SpecialCells method. Then
using the EntireRow property, select the rows containing these cells.
Ø Copy the constructed range into another sheet (suppose the sheet is named
‘TempSheet’) and work with the rows there.
Ø Delete the constructed range, and copy the edited range from TempSheet back
to the original sheet. Or, copy the edited range to the sheet where we are
compiling our data.
In the following code example, filtered rows will be copied to a temporary sheet
“TempSheet” from cell A1. TempSheet is in the same workbook where the
macro is located. The VBA code will look like this:
‘//** Copy filtered rows to temporary sheet **//
‘//** (This copies the header row and the filtered rows **//
rng.SpecialCells(xlCellTypeVisible).EntireRow.Copy _
(ThisWorkbook.Worksheets("TempSheet").Range("$A$1"))
4.3 Autofilter with VBA – Column positions unknown
Problem:
We have received a number of input files from our team mates. However, each
one of them has either inserted additional columns or rearranged columns for
his/her own analysis.
Solution:
Use the ‘Find’ technique we saw in section 3.2 to locate the text of column
names in the first row containing column headers. Get the column number of the
range returned by the Find method. Use that column number for ‘Field’
argument of Autofilter method.
Assumption:
Our colleagues have not changed the names of the columns and have not deleted
any of the columns that we require.
Code Example – Autofilter – Column positions unknown
Sub FilterRows()

‘//** Declarations **//
Dim wbk as Workbook
Dim strFilePath as String
Dim rng, rngVisible as Range
Dim nColSaleDate, nColSaleQty, nColSellerRegion as integer
Dim nColProductID, nTopLeftCell as integer

‘//** Partial Path of files **//
strFilePath = Thisworkbook.Path & “\Temp\SalesInput_”

‘//** Work with each workbook one at a time **//
For i = 1 to 3 step 1

‘//** Open the workbook SalesInput_i.xlsx **//
Set wbk = Workbooks.Open(“strFilePath” & i & “.xlsx”)

‘//** Set the range to refer to columns A to F on SalesData worksheet **//
Set rng = wbk.Worksheets(“SalesData”).Columns(“A:F”)

nTopLeftCell = rng.Cells(1,1).column

With rng.Rows(1)

nColProductID = .Find(“ProductID”).Column – nTopLeftCell + 1
nColSaleDate = .Find(“SaleDate”).Column– nTopLeftCell + 1
nColSaleQty = .Find(“SaleQty”).Column – nTopLeftCell + 1
nColSellRegion= .Find(“SellerRegion”).Column – nTopLeftCell +1

End With

‘//** Filter for Product ID **//
rng.AutoFilter Field:= nColProductID, Criteria1:="=*TAB*", _
Operator:=xlOr,Criteria2:="*LTP*"

‘//** Filter for SaleDate **//
rng.AutoFilter Field:= nColSaleDate, Criteria1:= _
">=4/1/2015", Operator:= xlAnd, Criteria2:= "<=6/30/2015"

‘//** Filter for SaleQty **//
rng.AutoFilter Field:= nColSaleQty, Criteria1:= _
">=10",Operator:= xlAnd, Criteria2:= "<=100”

‘//** Filter for SellerRegion **//
rng.AutoFilter Field:= nColSellRegion, Criteria1:= "North", _ Operator:=xlOr,Criteria2:=
"East”

‘//**------------------------------------------- **//
‘//** Proceed as in code example of section 2.4.5
‘//** ------------------------------------------- **//
Next i
End Sub




Notice that in the code example, even though the filtered range begins at column
A, we have calculated the offset of the filter column as if we don’t know the first
column of the range. Hence you can see code like:
nColProductID = .Find(“ProductID”).Column – nTopLeftCell + 1

This is done in order to show that this same code can be used for more general
type of range. For example, a range like “B5:M500”
This line of code is required because the ‘Find’ method returns a range whose
column number will be with respect to column A and not relative to the top
leftmost column of the filtered range. When applying Autofilter we need to
specify the column location with respect to the first column of the range.
Chapter 5
PivotTables and VBA
In this chapter, we will look at some useful ways to work with PivotTables
through VBA.
We will not deal with automatic creation of a PivotTable report because the
structure of a report, once the report is created and finalized, will usually remain
fixed. We will deal with the following two tasks which are to be performed
repeatedly and are hence are better candidates for automation. These tasks are
1. Updating the PivotTable with new data.
2. Reading data from an existing PivotTable so that it can be used for further
processing.
Keeping these two tasks in mind, there are only a handful of methods &
properties that are useful and these will be discussed as the need arises instead of
providing a full listing in certain other chapters.
5.1 Referring to PivotTables in macros
To work with a specific PivotTable in our macro, we need to find a way to refer
to it.
Every worksheet in an Excel workbook holds a collection of PivotTables. A
PivotTable created on that worksheet becomes part of the PivotTables collection.
A specific pivottable can usually be accessed using its Name. So, let’s first see
how we can set the name of an existing PivotTable.

5.1.1 Setting name of a PivotTable


Once a pivot table is created on a worksheet, we can set its name as follows:
1. Select any one cell inside the pivot table.
2. Right click to show the pop-up menu.
3. In the pop-up menu, select ‘PivotTable Options…’. This brings up the
PivotTable Options dialog box as shown in figure 5.1.1.
4. Enter a meaningful name in the first box labeled ‘Name’ and press Enter.
Now we can refer to the concerned PivotTable in our code using its name as
follows:
Wks.PivotTables(“PivotTable1”)


Fig 5.1.1 Setting a name for a PivotTable

5.1.2 Linking to a VBA PivotTable object


Once we can refer to a PivotTable on a worksheet, we should set it to a
PivotTable object in VBA. This object provides access to properties and methods
that allow us to operate on a pivotTable.
Here is the code for declaring a PivotTable in VBA and linking it to an existing
PivotTable on a worksheet.
Dim pvt As PivotTable
Set pvt = ThisWorkbook.Worksheets("Report").PivotTables(“PivotReport1”)

Let us look at that most important methods and properties of the PivotTable
object. The syntax can be seen in the next two sections when we mention code
examples.
Properties
1. Name: Returns a string holding the name of the PivotTable. We can set the
value of this property in a macro. However, it is always a good practice to set
the name manually and just read it in macros.

2. SourceData: Returns a string holding the sheet name and address of the range
on which the PivotTable is based.
If we are setting a value for this property in our macro, we would have to
append the name of the sheet containing the source data. Section 5.2,
“Updating bounds of a PivotTable in VBA”, will demonstrate how to do this.

Methods
1. RefreshTable: Refreshes the PivotTable (data and calculations) from its
source data.

2. GetPivotData: This method returns a range from within a PivotTable that
contains data from the pivot table. The range returned depends on the criteria
we specify as arguments.
5.2 Updating bounds of a Pivot Table in VBA
Problem:
We have a reporting sheet that has a pivot table report that refers to a range of
data. We compiled data on our worksheet and number of rows has increased.
Sadly, the original pivot table was not based on a Table. We need to update the
range of data in the pivot table.

Solution:
We update the SourceData property of the PivotTable.
For the code in this section assume that we have a PivotTable named
‘PivotReport1’ on a worksheet named ‘Report’.
For the code example below, assume that rngPivot is a range for the data created
using one of the methods described in Chapter 3. Here is the code for updating
the PivotTable:
Set pvt = ThisWorkbook.Worksheets("Report") _
.PivotTables("PivotReport1")
'//** Set the source data of pivot table to new range. **//
pvt.SourceData = _
rngPivot.Worksheet.name &"!"& rngPivot.Address
'//** Refresh the pivot table **//
Pvt.RefreshTable

Note that for this technique to work:


1. The PivotTable and its source range should be present in the same workbook.

2. If the source range is in another workbook, we have to specify the path of the
other workbook. Please see section 3.6.1 in Chapter 3 on how we can refer to
a range in another workbook.

3. If the source data is extracted from a database, use the method described in
Chapter 15.

5.3 Reading data from a Pivot Table with VBA
Problem:
Often we receive inputs workbooks containing pivot tables and we need to
extract values displayed in the pivot table and use them for further processing.
Solution:
We use the handy GetPivotData function in VBA.
For this section, consider the following example:
1. Our company sells three products (Pipe, Wire and Fittings), in four different
regions (North, East, West & South).

2. Each region has two sub regions A and B.

3. We get the demand data from all these regions for different products.

4. The demand will be of one of the two types. External demand from the end
customers and internal demand from various subsidiary and partner
companies.

Fig 5.3a Part of the demand data that we will use in this section

This data is summarized in a PivotTable using all 4 parameters (product, order


type, region and sub region). For the sake of demonstration, two different
summary functions (Sum and Count) have been used.
The resulting pivot table is shown in figure5.3c. The columns for subtotals for
“Internal” orders and the grand total columns on the extreme right cannot be
displayed due to space constraints but they are present and are shown in figure
5.3d.
The syntax for the GetPivotData method is as follows.
Set rng = pvt.GetPivotData(DataField,Field1,_
Item1,...Field 14,Item 14)
Arguments
1. DataField: This is a string holding the name of the summarized field. In our
example, the two data fields would be “Sum of Quantity” and “Count of
Quantity”. This is a required argument.

2. Field 1 to Field 14: These are optional string arguments and hold the names of
the parameter fields based on which we have summarized the data. In our
example we have 4 parameter fields (“Product”, “Order Type”, “Region” and
“Sub Region).A particular field can occur only once in the syntax.

3. Item 1 to Item14: These are optional arguments and hold the specific values
for the parameter fields. These are usually strings, but Excel allows for a
Variant datatype meaning that specifying numbers is possible.
In our example, if “Product” is a Field that is specified, Item can take one of
the values (Pipe, Wire and Fittings)
These are not entirely optional arguments, meaning if a ‘Field’ is specified, its
corresponding ‘Item’ has to be specified, else an error occurs.
Some importortant points about GetPivotData method:
1. This function returns a Range containing a single cell from within the
PivotTable.
2. The field names we specify as arguments must be displayed in the PivotTable.
if they are hidden the function returns Nothing
3. If all displayed fields and one summary function are specified as argument,
the function returns a range at the intersection of different fields that we
specify. The examples will make this point clear.
4. If any of the displayed field is omitted the grand/sub total is returned. This
also means that if the totals are not displayed, the function will return
‘Nothing’.
5. The names of ‘DataField’ and other Fields should be exactly as they appear in
the PivotTable. In our example, the default label for the Count column would
be “Count of Quantity” but it has been changed to “Count”
The arrangement of fields for creating the pivot table are shown in figure 5.3b.
Notice that the name of the “Count of Quantity” field has been changed to
“Count.

Fig 5.3b Arrangement of fields in the PivotTable.

Fig 5.3c PivotTable created to summarize demand data.



The remaining columns for sub totals and grand totals cannot be shown due to
space limitations. These are shown in figure 5.3d

Fig 5.3d Remaining columns of the pivot table shown in Fig 5.3c

The table that follows shows a number of different ways thet GetPivotData can
be used to extract information from a PivotTable. Study each example and
compare it with the table in figures 5.3c and 5.3d.


Syntax and parameters Range



Returned

All four parameters specified. $F$10


GetPivotData(“Count”, _
“Region”,North”,”SubRegion”,”A”,_
”Product”,”Pipe”,”OrderType”,“External”)

Sub total of rows (Notice that ‘SubRegion’ is not $C$15


specified)
GetPivotData("Sum of Quantity", _
"Region",South”, "Product",”Fittings”, _
"OrderType","External")

Grand total of rows (Notice that ‘Region’ & ‘SubRegion’ $H$19


are not specified)
GetPivotData("count","Order Type","External", _ "Product","wire")
Sub total of columns (Notice that ‘Product’ is not $J$7
specified)
GetPivotData("count", "Order Type", "External", _
"SubRegion", "A", "Region", "East")

Grand total of rows (for a particular group) $J$19


GetPivotData("count","Order Type","External")

Grand total of columns $S$12


GetPivotData("Sum of Quantity", "Region", "North")

Grand total of whole table $S$19


GetPivotData("Sum of Quantity")
Chapter 6
Sorting data in a Range or Table
In this chapter we will see how we can sort a range of data either in a rectangular
range or, a table.
We sort data using the Sort object of a worksheet. This object is returned by the
Sort property of a worksheet.In the following sections, we will take a look at the
important methods and properties of the Sort object.
6.1 Sort object of a worksheet
In the discussion that follows, we assume that we have already instantiated
a Workbook (wbk), a Worksheet (wks), and a Range (rngToSort)
To sort a range on a worksheet, we use the Sort object. This object represents the
most recent sorting that was done on the worksheet. The technical term used is
‘Sort State’. That sorting may have been done on some other range.
To sort a different range, we need to adjust the properties of the Sort object and
then call the ‘Apply’ method of this object. The Sort object of a worksheet is
obtained with the following code:
Wbk.Wks.Sort
We don’t need to set this to any object. We can use it directly inside a
’With’block and invoke all the properties and methods.
With Wbk.wks.Sort
‘…………………………………….
End with

The SortState can be seen in the Sort dialog that appears when we select “Sort”
option from the “Data” menu. This dialog is shown in figure 6.1a

Fig 6.1a Sort dialog


Properties
1. Header: Specifies whether the first row of our range contains column
headers. Its value is one of the three xlYesNoGuessconstants that are
predefined in Excel.
We will always use a range with column headers, so in our case the value of
this property will always be xlYes.
Setting a value of this property to xlYes is similar to clicking in the check box
marked with number “1” in the ‘Sort’ dialog shown in figure 6.1a.
2. MatchCase: This property takes a Boolean value that specifies whether the
sorting should be case sensitive or not.
A value of ‘TRUE’ will indicate case sensitive sorting. That means, for a
descending order sorting, all cells starting with a capital letter will be on top
all cells starting with a lower case letter having the same value. The values
however will be correctly sorted. All a’s will always be above all b’s. We will
usually set this property to ‘False’.
3. SortFields: This property returns the ‘SortFields’ collection and is the most
important property described so far.
Each SortField object represents a column in our range of data. Each can be
sorted in ascending or descending order.
We describe the SortFields collection in more detail in coming sections.
4. Orientation: This property specifies if the rows or the column in the
concerned range should be sorted. We intend to work only with data arranged
in tabular form with each row containing a record of interest.
Hence we will always sort by rows. We leave the value of this property at its
default value of xlTopToBottom.
To set the “MatchCase” and “Orientation” properties manually on the worksheet
we first have to display the “Sort Options” dialog by clicking on the “Options”
button (marked with number “2” in the ‘Sort’ dialog shown in figure 6.1a). Then
we can select the appropriate options.

Methods
1. Apply: This method sorts the range based on the Sort State parameters that
are specified using the properties of the Sort object.

2. SetRange: This method is used to specify the range which will be sorted.
Note that the ‘SetRange’ Method should be called before we can use the ‘Apply’
method.
6.2 SortFields collection
As mentioned earlier, this collection is a group of all the SortField objects, each
of which represents a column that will be sorted. The properties of this collection
are not that important but the only two methods it has, are highly important.
Methods
1. Add: This method adds a new column as a criterion on which the concerned
range will be sorted.
Intuitively, on a worksheet, the method represents clicking the “Add Level”
button on the Sort dialog box (marked with number “3” in the ‘Sort’ dialog
shown in figure 6.1a). Syntax of the method is as follows:
With Wbk.wks.Sort
.SortFields.AddKey, SortOn, Order, _
CustomOrder, DataOption
End With

This is one of those methods where it is better to specify the arguments by


name instead of depending on position. Names of the arguments are exactly
as shown in the syntax. Examples provided in this section will clarify the
procedure.
Arguments:
a) Key: This is a Range object. It specifies the column in that will be used
for sorting.
A very importantfact to note: the key range should not include the cell
from the header row.

b) SortOn: This argument specifies what the part of cell should be considered
for sorting. The value can be one of the following 4 xlSortOn constants.
Value Rows sorted based on:
SortOnValues Value contained in the cells in each row in the Key
column.
This is the most common scenario and this is what
we will use.
SortOnCellColor Color of interior of cells in each row in the Key
column.
SortOnFontColor Color of font used in the cells in each row in the
Key column.
SortOnIcon Icon contained in the cells in each row in the Key
column

c) Order: This argument has two possible values both of which are defined
constants. It specifies the order in which the field will be sorted.


Value Rows sorted
xlAscending Ascending order. This is the default value
xlDescending Descending order.

d) DataOption: This argument has two possible values. Both of which are
defined constants.
Value Rows sorted
xlSortNormal Sorts numeric and text data separately. This is the
default value
xlSortTextAsNumbers Treat text as numeric data for the sort.

Fig 6.2a shows the sorting that happens for different values of DataOption.

Fig 6.2a Effect of different values of ‘DataOptions’ parameter



2. Clear: This method is used to remove all the existing SortFields so that new
ones can be added. This method should be called before using the Add
method to add more sort fields.

Let’s see the code for sorting the range that we mentioned at the beginning of
this section. Recall that we assume we have already instantiated the following:

1. a workbook object : wbk
2. a worksheet object : wks
3. a range object : rngToSort
Suppose we have to sort with the 2nd column in ascending order and the 5th
column in descending order.
Below is a sub-procedure that would accept the range to be sorted as an
argument. Notice two things in the code:
1. The worksheet of the concerned range is obtained by using the ‘Worksheet’
property so we don’t need to pass it as an argument.

2. When setting the Key argument of the sort field we have worked out the range
using the Offset function.

The offset of one row is used to skip the header row and reach the first starting
cell of the data. We then resize that one cell range to contain all the data rows
(total rows-1) and 1 column

We make no assumption about the location of the header row in the range to be
sorted. The header row could be in row 1 or any other row.

Sub SortRange(ByRef rngToSort as Range)
Dim lRows as long
lRows = rngToSort.Rows.Count
With rngToSort.Worksheet.Sort
‘//** Clear the current sort fields so we can add new ones **//
.SortFields.Clear

‘// Set the range to be sorted **//
.SetRange(rngToSort)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
‘//** Add the two sort fields **//
.SortFields.Add _
Key:= rngToSort.Cells(1,2).Offset(1,0).Resize(lRows-1,1), _
SortOn:= SortOnValues, _
Order:= xlAscending

.SortFields.Add _
Key:= rngToSort.Cells(1,5).Offset(1,0).Resize(lRows-1,1), _
SortOn:= SortOnValues, _
Order:= xlDescending

‘//** Begin the sort **//
.Apply
End With
End Sub



6.3 Sorting a range – Column position unknown
Problem:
We have received a number of input files from our team mates. However, each
one of them has either inserted additional columns or rearranged columns for
his/her own analysis.
We need to sort the range on certain sheets before we begin working on the data.
Solution:
First create the range for the data using techniques described in chapter 3.
Now, use the ‘Find’ technique we saw in section 3.2 on the first row of the range,
to locate the text of column names in row 1. Use that column number for
working out the Key argument.
Assumption:
Our colleagues have not changed the names of the columns and have not deleted
any of the columns.
Important note:
When specifying the Key range for SortField, we need to find the column’s
position relative to the first cell in the range that has to be sorted. The Find
method used to locate the column gives the column relative to column A in the
worksheet. Hence the relative column position is found using the code:
(rngKey.Column) -rngToSort.Cells(1,1).Column + 1

And now the Sub for sorting the data


Sub SortRange(ByRef rngToSort as Range)


Dim lRows, lngKeyCol as long
Dim rngKey as Range
lRows = rngToSort.Rows.Count
With rngToSort.Worksheet.Sort
‘//** Clear the current sort fields so we can add new ones **//
.SortFields.Clear

‘//** Set the range to be sorted **//
.SetRange(rngToSort)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom

‘//** Add the two sort fields **//
Set rngKey = rngToSort.Rows(1).Find(“Col1_Name”)
lngKeyCol = rngKey.Column-rngToSort.Cells(1,1).Column + 1
.SortFields.Add SortOn:= SortOnValues, _
Key:= rngToSort.Cells(1,lngKeyCol). _
Offset(1,0).Resize(lRows-1,1)
Set rngKey = Nothing
Set rngKey = rngToSort.Rows(1).Find(“Col2_Name”)
lngKeyCol = rngKey.column-rngToSort.Cells(1,1)
.SortFields.Add SortOn:= SortOnValues, _
Key:= rngToSort.Cells(1,lngKeyCol). _
Offset(1,0).Resize(lRows-1,1)

‘//** Begin the sort **//
.Apply
End With
End Sub


Chapter 7
Handy worksheet formulas and
functions

7.1 The power of Array Formulas…
We need to start this part with an example.
Do the following, in the exact order as stated here (it’s better if you do these
steps. If you don’t want to do them, please refer to the screenshot on the next
page while reading)
1. First, on a worksheet, select the cells A1 to E1 (5 cells).
2. Click in the Formula bar and type (Don’t forget the curly braces)
={3.01,5.5,4,1,2.3}

3. After typing, hold down the Ctrl and Shift keys together and hit Enter.
What do you see?
We see that Excel puts curly braces around the entire formula, including the ‘=’
sign. Also that the numbers are now distributed one in each of the selected cells.
Now, do the following, in the exact order as stated here
1. First, on the same worksheet, select the cells A3 to E3 (5 cells).
2. Click in the Formula bar and type (Don’t forget the curly braces)

=IF(A1:E1>3,"Yes","No")

3. After typing, hold down the Ctrl and Shift keys together and hit Enter.
What do you see?
We see that Excel puts curly braces around the entire formula, including the ‘=’
sign.
Also, each number in the cells A1 to E1 is compared independently with the
number 3 and, then if it is greater than 3, a “Yes” is placed in the corresponding
cell in row 3, else a “No” is placed there.
Meaning, it works like this:
“Is the value in Cell A1 (the First cell in input range) > 3 – Yes – Place “Yes” in
the First cell in the selected range where the formula was entered”
“Is the value in Cell B1 (the Second cell in input range) > 3 – Yes – Place “Yes”
in the Second cell in the selected range where the formula was entered”
And so on…
“Is the value in Cell E1 (the Fifth cell in input range) > 3 – No – Place “No” in
the Fifth cell in the selected range where the formula was entered”
Say Hello to Array Formulas. A.K.A “Ctrl-Shift-Enter” or “CSE” formulas.

Notes on the figure 7.1a:
· The cells that were selected for entering the array formulas are given a
thick dark border
· Column G shows the formula inserted in the respective rows. If we click
in any cell inside the blue outline, we should see that formula in the
formula bar.

Fig. 7.1a Array formulas entered using Ctrl + Shift + Enter

Array Formulas are written just like regular Excel formulas, using some of the
same Excel worksheet functions. But, they differ in three important ways:
1. Where regular formulas take single cell values or a range as input for
calculation, these formulas always take a range.
2. Regular formulas produce a single result in a single cell. CSE formulas
produce an array of results. Length of the array being same as length of array
supplied as input.
3. Regular formulas are completed by hitting the ‘Enter’ key. CSE formulas
require “Ctrl + Shift + Enter”
Take note that these are not called Array formulas because they take an Array or
Range as input. It is because they produce an Array as their result.
The nice thing about them is that the resultant array can be fed into any regular
formula that takes an array for input.
For example, continuing with the example we used above, on the same
worksheet, select cell A5 and enter the following in the formula bar:
=SUM(IF(A1:E1>3,1,0))

After typing, hold down the Ctrl and Shift keys together and hit Enter.
The ‘IF’ part of the formula is similar to our earlier example, except that instead
of returning strings “Yes” and “No” , now it returns numeric values 0 and 1. This
portion returns an array.
The resulting array is numeric. Hence it can be fed as input to any regular
formula or function that takes an array as input. ‘Sum’ is one such function. It
takes an array of numbers and adds all of them up.
The IF function in our array formula produces an array of 0 and 1 and then Sum
takes up that array and adds all the elements. The result is a singlevalue showing
how many numbers in cells A1 to E1 are greater than 3.
v What if we only press ‘Enter’?
For the formulas to work, we had to tell Excel to treat the input ranges as arrays.
Hence Excel applies the function on each element one at a time. We
communicate this need to excel by pressing “Ctrl + Shift + Enter”.

If we just press “Enter”, Excel would just work with the first element of the
array and would produce a result for a single cell.
And there’s more:
Now let’s look at the figure again (sorry to make you turn pages) and focus on
row 7 and below. In column G we see formulas with more than one array.
Cells A7 to B9 have some numbers. This is how the Excel works when we select
cells D7:D9 and enter the formula shown in G7.
“Take the first value (the value in A7), in the array A7:A9 add this to the first
value in the array B7:B9 (the value in B7), this gives the first value in our result
Array”
“Take the second value in the array A7:A9 (the value in A8), add this to the
second value in the array B7:B9 (the value in B8), this gives the second value in
our result Array”
“Take the third value in the array A7:A9 (the value in A9), add this to the third
value in the array B7:B9 (the value in B8), this gives the third value in our result
Array”
“Cells D7:D9 were selected for entering the formula, so paste the result array in
this range, one value per cell”
In this example we have added the array values, but yes you are right, we can
subtract, multiply and divide as well (provided none of the values used as
denominators are zero).
As mentioned earlier, we can feed the resultant array into a function that accepts
an array as input. That is exactly what is done in cell D11. Please note that we
can use more than 2 arrays as well, only requirement is that they should be of the
same size (in our example, both arrays have 3 cells each).
Orientation matters:
The placement or display of the results of an array formula in the cells of a
worksheet, the result array, depends on the input array and the selection made at
the time of entering the formula. The following points should be borne in mind:
Arrays entered directly in the formula bar are always returned as horizontal row.
So if we select cells A1:A5 and then type
={3.01,5.5,4,1, 2.3}

We will get only the value “3.01” in the cell A1. Now consider the Figure 7.1b
Fig. 7.1b Array formulas with two arrays as arguments

Like before, the cells that were selected at the time of entering the array formulas
are marked with a thick dark border.
Column F shows the array formulas entered in the selection and the lightly
colored cells are simply numeric values.
In each of the formulas, one input array is vertical (A4:A6) the other is
horizontal (B1:D1).We observe the following:
1. When a row is selected for entering the formula, the horizontal array is given
importance.

The first cell from the vertical array is added to each cell of the horizontal array.
The resulting array is horizontal.

2. When a column is selected for entering the formula, the vertical array is given
importance.

The first cell from the horizontal array is added to each cell of the vertical array.
The resulting array is vertical.

3. If we select a range of ‘h’ rows by ‘w’ columns (where h is the number of
cells in the vertical array and w is the number of cells in the horizontal array),
then every combination of row and column cells is calculated.
The first cell of vertical array, is added to every cell of horizontal array and
becomes the first row of the resulting array.
Similarly, the second cell of vertical array, is added to every cell of horizontal
array and becomes the second row of the resulting array.
In our example ‘h’ and ‘w’ both have the value 3 giving a 3x3 range of cells.
The good news:
In most practical applications we have to deal with arrays that are fed into other
Excel functions that would then return a single value. Hence, it would not matter
whether the resulting array is vertical or horizontal.
7.2 …And why we use them only sparingly
Here are some reasons why array formulas are not preferred in spite of their
versatility and low memory consumption:
Formulas taking fixed length arrays as inputs
Ø In figures 7.1a and 7.1b, all the arrays that were used as input to array
formulas were of fixed length. This means whenever a new row or column of
data is added, we have to manually update each and every formula. We would
have to extend the range in the input as well as output.

The solution to this problem would be to use a variable named range that
would expand and contract as new data gets added.
Formulas returning fixed length arrays as output
Ø In figures 7.1a and 7.1b, all the array formulas returned arrays of fixed length.
These arrays are similar to array constants. All we can do with them is to paste
them across a range of cells or use them as inputs to some other functions.

Ø We cannot use these arrays for purposes like creating a validation list, or as a
chart series.

The solution to these problems is to use formulas that return a range of cells
rather than an array. Unlike arrays, variable length ranges can be created using
formulas and they can be named and stored in the workbook. We can use
ranges very easily for creating cell validation and chart series.
We will learn how to create variable length named ranges in Chapter 10
“Dynamic Named Ranges”. But before that we will see some of the functions
that take numbers or strings as input and return a range.
7.3 Index
This function provides us with either a single cell or a group of cells (an array)
that we can use further in functions.
The most common form of this formula is:
=INDEX(reference, row_num, [column_num], [area_num])

Arguments:
1. reference:This is address of one or more ranges (mainly rectangular).
If multiple disjoint ranges are specified, they are included in a comma
separated list. In that case, each range is called an area and can be selected by
the area_numargument.
Examples:
· Single range
=INDEX($A$1:$G$25, [row_num], [col_num], [area_num])

· Multi-range
=INDEX((A1:G25, I5:K45, A26:G51),[row_num],[col_num],[area_num])

We can see that the ranges specified for multi-range format, don’t have to be
of the same size. Meaning they don’t need to have the same number of
rows/columns.

2. row_num: This is the number of a single row from the specified range (or
selected area, in case of multi-range formula) from which the value has to be
taken.
If the column number is omitted, then specifying this argument becomes
mandatory. In that case, the formula returns the full row from the range.
Width of the row (number of cells in it) is equal to the number of columns in
the input range.
3. col_num: This is the number of column from the specified range (or selected
area, in case of multi-range formula) from which the value has to be taken.
If the row number is omitted, then specifying this argument becomes
mandatory. In that case, the formula returns the full column from the range.
Height of the column (number of cells in it) is equal to the number of rows in
the input range.
If both, ‘row_num’ & ‘col_num’ are specified, Index returns a single cell at
intersection of the specified row and column. Let us consider few examples.
4. area_num: This argument is applicable only in multi-range scenarios. It is the
number of the range from which the value will be taken.
The ranges are specified in the referenceargument in the form of a comma
separated list enclosed in brackets (). Each such range is called an area. The
first area is Area 1, second is Area 2 and so on.
If only a single range is specified, the area_num argument becomes
unnecessary.

Fig 7.3a Sample data for using Index function



The table below shows the different ways of using the INDEX function. The
formulas are used on the cells shown in figure 7.3a.
To Select
Enter formula Effect
cell(s)

=INDEX(A1:C9,3,) Full row of data from the input
E2:G2
range
(use Ctrl+Shift+Enter)

=INDEX(A1:C9,,1) Full column of data from the
I1:I9
input range
(use Ctrl+Shift+Enter)
E6 =COUNTA(INDEX(A1:C9,4,))
Count of entries in 4th row of the
input range
G6 =SUM(INDEX(A1:C9,,3))
Count of entries in 3rd column of
the input range
Value in one single cell of the
F5 =INDEX(A1:C9,3,1) input range
The examples are for single range specified in the ‘reference’ argument. We will
see the use of multiple ranges in section 7.14.
7.4 Match
The match function takes a value (string, number or address of a cell holding a
value) and an array (either a range address or a manually typed array) and one
additional parameter as arguments. It then finds the value in the array and returns
the position of the value in that array.
The first element in the array is position 1, second one is position 2 and so on.
Syntax for entering the function is as follows:
= MATCH(lookup_value, lookup_array, [match_type])

Arguments:
1. lookup_value:This is the value that we are trying to find in the array. It could
be a constant value that we type in, or address of a cell from which to refer
the value.

2. lookup_array: This is the array in which we want to find the value. The array
can be a range on a spreadsheet or an array that we type in by hand. It has to
be one dimensional-single row or column of cells. We can’t specify a
rectangular group of cells.

3. match_type: This parameter specifies how Excel will match the value inside
the lookup array. Its value can be one of the 3 values listed in the table below.
The default value is 0.

Match Excel does the following
Type
Most preferred value used in order to avoid logical errors.
Returns the position of a value in the lookup array that exactly
0
matches the lookup value. Lookup array need not be sorted in
any manner.
Returns the position of the largest value that is less than or equal
to the lookup value.
1
The lookup array should be in sorted in ascending order for this
parameter to work.
Returns the position of the smallest value that is greater than or
equal to the lookup value.
-1
The lookup array should be in sorted in descending order for
this parameter to work.

Return Value:
Match function returns the position of a value in the array. It does not return the
value itself.
If a correct matching value is not found, the function returns “#N/A”
Let’s see a few examples that illustrate different match types. We will consider a
spreadsheet setup shown in the Fig 7.4. the value ‘c3’ in the cell E2 is for use in
the ‘Indirect’ function that we describe in the next section.

Fig. 7.4 Sample data for using Match function


· =MATCH(2.95,A3:E3,-1)

In this case, Excel starts with cell A3 and goes towards cell E3 looking at each
value one at a time. Notice that the values are arranged in descending order.
Value 2.9 in C3, is lower than the lookup value 2.95. The only value that is
higher than 2.9 but still lower than the values seen so far is 3.1 in cell B3.
Although 4.5 is also greater than the lookup value, there is a value in the array
that is lower than 4.5. Hence that value, the one in B3, is taken as the match
value.
This value is in the second position in the array. So the return value is 2.
· =MATCH(2.95,A1:E1,1)

Notice that the values are arranged in ascending order.


Value 3.1 in D1, is greater than the lookup value 2.95. The only value that is
lower than 2.95 is 2.9 in cell C1. Although 2.8 and 2 are also lower than the
lookup value, there is a value in the array that is greater that these two, so that
value, the one in C1 is taken as the match value.
This value is in the third position in the array. So the return value is 3.
· =MATCH(2.95,A1:E1,0)
In this case, there is no value in the array that is exactly equal to 2.95. so the
function returns an error (#N/A or Not Available)
· =MATCH(2.8,A1:E1,0) ‘
In this case, there is a value in the array that is exactly equal to 2.8. So the
function returns its position within the array
7.5 CountA
This function gives a number of cells in a range that are not empty. The function
counts text, numbers, error values and empty strings (“”), when these are
returned by a formula.
Syntax for this function is as follows:
= CountA(rngReference)

Arguments:
rngReference: This is the range in which we want to count the non-empty cells.
The best use of this function is to find out the dimensions of a range of cells.
Example 1:
Consider the weekly table of Revenues and Expenses shown in Fig 7.5a.

Fig 7.5a Sample data

Every week we enter a new column to the right. If at any time, we wish to find
the number of weeks entered, we just use the following formula
=CountA(1:1)

So the total number of columns in the table is given by the above formula. If we
want to include one empty cell A1, the formula would be:
‘//** To include the one empty cell in row 1 **//
=CountA(1:1) + 1

This gives us the dimension of a range that can be used in other functions.
For this formula to serve its intended purpose, the requirements are :
1. There should not be any blank cell in between two non-empty cells, at least in
the first row.
2. There should not be any content after the extreme right column otherwise that
too would get counted.
We can do something similar when the data is arranged in columns. This is
shown in the next example
Example 2:
Consider the weekly table of Revenues and Expenses shown in Fig 7.5b.
Every week we can enter a new row at the bottom.

Fig 7.5b Sample data

The number of weeks entered is given by:


=CountA(A:A)

This is also equal to the total number of rows entered so far.


If we want to include one empty cell A1, the formula would be:
=CountA(1:1) + 1

This function will play a much more important role when we create self-
expanding named ranges in Chapter 10
7.6 Indirect
The Indirect function takes a string value representing a cell address and returns
a reference to that address.
If entered into a cell, this function returns the value contained in the cell
represented by the string. The real power of this function is seen when we want
to select the range on the fly based on values selected by the user.
Syntax is as follows: = INDIRECT(strAddr, [a1])
Arguments:
1. strAddr: This is the string representing the cell or range address. It should
include the sheet name if the cell is on a sheet different than the one where the
formula is entered.

2. a1: This is an optional Boolean value that specifies if the reference mentioned
is in A1 format or R1C1 format.
The default value for this argument is ‘True’, meaning the string represents
A1 style reference. We will mostly deal with this case as it is more intuitive
when using a worksheet.

Fig 7.6 Sample Table for Indirect function.

Consider the table shown in figure 7.6. Let’s see some of the formulas and their
effects.
‘//** returns 2.8 **//
· =INDIRECT("B1")

‘//** returns 2.9 **//
· =INDIRECT(E2)
In this case the cell E2 contains string ”C3” that acts as the input string. The cell
C3 contains the value 2.9. The first formula below has a string in R1C1 format.
‘//** returns 3.1 **//
· =INDIRECT("R3C2",FALSE)

‘//** returns 5 **//
· =INDIRECT("’Range Values’!B5")

We use the function to fetch a value from a cell on nother worksheet named
“Range Values”.Notice that since the sheet name has a space, we need to enclose
the sheet name in single quotes (‘)
· =Average(INDIRECT(A5&"!$B$5:$B$505"))

The formula above takes the name of the sheet from the value of cell A5, then it
fetches the range “B5:B505” from that sheet and averages the values
therein.Notice that the sheet name can be entered by the user in cell A5.
7.7 Text functions – Trim, Clean, Text
Trim
The Trim function is used to remove extra blank spaces from the beginning and
end of a string. Consider the example in figure 7.7

Fig 7.7 Example of Trim function

The most important use of this function is when creating input for functions like
Vlookup where extra spaces can cause a value to appear as a mismatch. An
example can be found in the section for “Text” function.

Clean
The Clean function is used to remove some non-printable characters from text.
This mostly happens when text data is imported from some other program into
Excel.
Most of the times this should be used in conjunction with Trim. An example is
given in the section for “Text” function.
Important note:
Note that the Clean function can only remove around 32 non printable
characters. (Technically speaking, those with ASCII values from 0 to 31).
It cannot remove non-printable characters generated by programs like text
processors that support multiple languages. An example would be Data copied
from Word files having Chinese or Japanese characters.
Another annoying situation is when data is copied from websites. Often to
separate text, websites use a special character denoted by “&nbsp;” (called a
‘Non-breaking space’) in the web page’s html code.
When we copy data from the website either manually or using another software,
this character may creep in along with the other character data. Unfortunately,
this character cannot be removed using Trim or Clean
Text
The TEXT function converts a numeric value to text and lets you specify the
display formatting by using special format strings.
Major applications of this function deal with display of data on a worksheet.
Since we are more interested in analyzing and summarizing data, we will cover
only one application that helps with looking up data in a range of cells.
If we have a reporting worksheet where we need to format specific cells we can
use the formatting options available on the “Home” tab.
We begin with a description of a problem and then, use the TEXT function as a
solution to that problem.
Problem:
Suppose that our organization uses 5-character alphanumeric product codes.
Unfortunately, not all codes are a combination of numbers and letters. Some of
them are just numbers. To make things worse, some of these numeric codes have
leading zeros. For example, “00063”.
We have a workbook (Product Master) that has a listing of all the products. In
this workbook, the codes are listed in the correct format (“00063”) in column A.
The Product Names are listed in column B in this workbook.
However, we have a sales database from which we get a daily report extracted in
plain text format. In this file, the codes are not correctly extracted. The code
“00063” gets listed as “63”. The file has two columns, product code in column A
and units sold in column B.
Output from these two sources is shown in the following figures.

Table from “Product Master” Table from Sales Database


In the sales file we need to lookup up the name of the product using the product
code.
Solution:
One approach would be as follows
1. Put this formula in cell C2
=Text(Trim(Clean(a2),”00000”)
2. Copy this formula in all rows of column C. This will convert all numeric
values into 5-character text strings with leading zeros.
3. Select the entire column C, and paste the values in column A.
4. Delete the contents of column C.
5. User Vlookup and bring in the product names in column C.
Another approach would be as follows
1. Put this formula in cell C2 in the table from Sales Database
=Vlookup(Text(Trim(Clean(a2)),”00000”), _
[ProductMaster.xls]Sheet1!$A:$B,2,0))

2. Copy this formula in all rows of column C. This will bring in the proper
product names from the other file. Of course the original values in column
A remain unaffected.
7.8 Conditional Math - COUNTIFS, SUMIFS,

AVERAGEIFS
These functions calculate the count, sum and average of cells in one range, but
only if those cells meet certain criteria specified in a set of other ranges.
For our examples we will consider the table shown in figure7.8

Fig. 7.8 Sample data for conditional math functions

We will see the use of formulas by specifying two criteria, but Excel allows as
many as 127 different criteria to be specified.
For each function we will consider some scenario. The criteria to be met will be
specified in cells B2 and B3, and the result of the formula will be shown in cell
G2.
We will use the first function (COUNTIFS) to gain a deeper understanding of the
different ways available for specifying the criteria.

COUNTIFS
This function counts the number of times the specified criteria are
simultaneously met across different ranges. The syntax for the formula is as
follows:
=COUNTIFS(criteria_range1, criteria1, _
[criteria_range2, criteria2]…)

“criteria_range1, criteria_range2…” These are the ranges that contain values that are to be
checked against the criteria.
“criteria1, criteria2…” These are the actual criteria that would be checked.

How to specify a criterion? Criteria can be specified as:


1. String: This should be an expression with the operator and the value for
comparison.
Examples are: “=500”, “<=900”, “=OnHold”, “<>Rejected” and so on.
We can also form the string by concatenating a constant string and a value in
a cell. For example: “>”&B5, cell B5 may hold a value like 500.

2. A value: If a value is provided for criteria. The Criteria range will be checked
for values being equal to the criteria value.
Examples are: 50, 1000, “OnHold”, “Approved” and so on.

3. A Cell Reference: If a cell reference is provided, the cell should contain
either a criteria string or a value. In this case we don’t need to provide an
operator in the formula. We can place the operator inside the cell itself.
Using wildcards in criteria:
When we are specifying criteria for a range that contains text values, we can use
wildcard characters as placeholder for one or more characters for which we can
accept any value. The wildcard characters are:
1. ‘*’ for multiple characters (zero or more)
2. ‘?’ for single character.
Towards the end of this section we will see examples that will make this concept
clear.

Scenario A - Criteria for numeric values


Suppose, for the 8-week period, we want to count the number of weeks when the
production was above 1300 units but the demand was below 900 units.
Method 1:
One formula could be as shown in figure 7.8a:
=COUNTIFS(B10:I10,">"&B2, B8:I8,"<"&B3)

Cells B2 and B3 can contain the values 1300 and 900 respectively. The result
would be 3. There are 3 weeks where production was above 1300 AND when
demand was below 900.
For easy reference, the formula entered in cell G2 is shown expanded in the cell
below it.

Fig. 7.8a Specifying criteria by concatenating a string with a cell reference

Method 2:
Another method is shown in figure 7.8b. Notice how we have provided the cell
reference, B2 and B3, in place of criteria and notice the content of those cells.
The result, obviously is the same.

Fig. 7.8b Specifying criteria with cell reference

Obviously, if we provide a cell reference and the cell does not have an operator,
then, only those cells are counted that exactly match the value in the reference
cell.
We have seen the operators Equal to (=), Less than (<), and Greater than (>)
being used in the function. We can also use the operators Less Than or Equal to
(<=), Greater Than or Equal to (>=), Not Equal to (<>). For example, for the
table in figure 7.8b, the following formula will give a value of 4
=COUNTIFS(D10:K10,"<=2190",D8:K8,"<900",D10:K10,"<>900")

It gives a count of all the columns(weeks) which satisfy the following criteria
1. Production (row 10) is less than or equal to 2190 but also, not exactly equal to
900
2. Demand (row 8) is less than 900

Scenario B - Criteria for text values


For this section consider the table shown in figure 7.8c.

Fig. 7.8c COUNTIFS function with text values

Suppose we want to find:


How many rows have “nos.” in the ‘Measure’ column (col. E) but don’t have the
word “bolt” in the ProductName column (col. B)?
The formula to use would be:
=COUNTIFS(E1:E9,"=nos.",B1:B9,"<>*bolt*")

The result is 3, because only rows 3, 4, 5 and 9 have a Measure of “nos.” and out
of these only row 5 has the word “bolt” in the ProductName.
Note how we have used the wildcard character “*” in the formula. The formula
will consider all entries in column B that contain the word ‘bolt’ with any
number of characters before or after that word. These characters can include
blank spaces. Also, there may not be any character before or after “bolt”.
Meaning the entries beginning and ending with “bolt” are also considered when
counting.
If we had used “*bolt”, Excel considers only the entries that end with the word
“bolt”. Similarly, “bolt*” would consider only the entries that begin with the
word “bolt”.
Now let us look at a case of using “?” wildcard. We will also specify criteria for
a mix of text and numeric columns.
Suppose we want to find:
How many rows have a value above 100 in the ‘Units Sold’ column (col. D)
where the enntry in the ProductName column (col. B) ends with “wir” and just
one more character?
The formula to use would be:
=COUNTIFS(D1:D9,">100",B1:B9,"=*wir?")

The result is 2, because only rows 6, 7 and 9 have values above 100 in column D
and characters “wir” in column B. And, out of these only rows 6 and 7 have just
one character after “wir”.

AVERAGEIFS
This function calculates the average of numerical values in one range based on
whether specified criteria are simultaneously met across different ranges. The
syntax for the formula is as follows:
=AVERAGEIFS(average_range, criteria_range1, _
criteria1, [criteria_range2, criteria2]…)

“average_range” This is the range that contains the numeric values that are to be
averaged
“criteria_range1, criteria_range2…” These are the ranges that contain values that are to be
checked against the criteria.
“criteria1, criteria2…” These are the actual criteria that would be checked

The methods for specifying the criteria_range and criteria are same as those for
COUNTIFS function.

SUMIFS
This function calculates the sum of numerical values in one range based on
whether specified criteria are simultaneously met across different ranges. The
syntax for the formula is as follows:
=SUMIFS(sum_range, criteria_range1, criteria1, _
[criteria_range2, criteria2]…)

“sum_range” This is the range that contains the numeric values that are to be
summed
“criteria_range1, criteria_range2…” These are the ranges that contain values that are to be
checked against the criteria.
“criteria1, criteria2…” These are the actual criteria that would be checked

The methods for specifying the criteria_range and criteria are same as those for
COUNTIFS function

7.8.1 Precautions and some useful points


Here are some points we should bear in mind while using conditional math
functions.
1. All the ranges (sum_range, average_range and criteria ranges) specified in
these functions should be of the same size. Meaning they should contain the
same number of cells whether they are aligned in a row or column.

2. The ranges can be located on different worksheets or in different workbooks.

3. The ranges don’t need to start on the same column or row.
For example, in a workbook named “Book1”, in SUMIFS, if the sum_range is
A2:A10 on Sheet1, criteria_range1 can be D1:D9 on the same sheet,
criteria_range2 can be Z2:Z10 on the Sheet2 in the same workbook, while
criteria_range3 can be J3:J11 on the Sheet3 in another workbook named
“Book2”.
4. All the ranges (sum range, average range and criteria ranges) should be
containted either in columns or rows not both. For example, if sum_range is
A1:A10 and criteria_range1 is B1:K1 the function returns a “#VALUE” error.
7.9 Offset
The Offset function returns a reference to a range of cells located at a distance of
specified numbers of rows and columns from another cell.
We can specify the number of rows and columns that should be returned.
The distance we specify is that of the top left cell in the returned range from the
reference cell. Examples will clarify this point.
Offset function can return any one of the following:
1. A single cell, in which case the cell where the formula is entered will contain
the value in the returned cell.
2. A horizontal or vertical array of cells.
3. A rectangular range of cells.
If we ask for an array or range of cells, we need to enter the formula as an array
formula.
Also, the returned array can be fed into a regular function or formula that takes
an array as input. Syntax is as follows:
=OFFSET(reference, rows, cols, [height], [width])

In the explanation that follows,


· ‘target range’ means the range that will be returned by this function.
· ‘target cell’ means the top left cell of the target range.
Arguments:
1. reference: This is the address of the cell that we use as the reference point. If
we specify a range of cells, then the top left cell in that range is used as
reference and distance is calculated from that cell.

2. rows: This argument specifies the number of rows till the row of the top left
cell of the target range. Positive values indicate that target cell is below the
reference. Negative values indicate that target cell is above the reference.

3. cols: This argument specifies the number of columns till the column of the top
left cell of the target range. Positive values indicate that target cell is to the
right of the reference. Negative values indicate that target cell is to the left of
the reference.

4. height: This argument specifies the number of rows that we want Excel to
return in the target range. One of the rows is the row of the target cell.
This is an optional argument. Its use will become clear in the examples. It
should be noted that ’height’ is always positive. Meaning, if we specify a height
value of 3, the target range will have 3 rows, two of which will be below the
target cell.

5. width: This argument specifies the number of rows that we want Excel to
return in the target range. One of the columns is the column of the target cell.
This is an optional argument. Its use will become clear in the examples. It
should be noted that ’width’ is always positive. Meaning, if we specify a width
value of 3, the target range will have 3 columns, two of which will be to the
right of the target cell.
Example:
Consider the spreadsheet shown in figure7.9

Fig 7.9 Example of Offset Function

The rows 5 to 7 hold revenues and expenses for each month. Rows 10 to 13,
summarize the revenues by quarter. The top left cell of the table is A5.
Formula in cell B10 is nothing too brilliant; it sums an array of cells returned by
the Offset function. The array starts one row below and one column to the right
of cell A5. It is 1 row high and 3 columns wide.
Formulas in cells B11 to B13 show us the power of the Offset function when
combined with a little bit of arithmetic. These formulas calculate the address
(column offset, to be precise) of the target cell using the quarter number in
column A on the same row. Here is how it works
The first quarter starts at column B, at an offset of 1 column. For each quarter
after Q1, a minimum offset of 3 is required, plus 1 that is already needed.
Quarter Starts at Calculation
(column offset from for offset
A5)
1 1 0 + 1 (1-1)*3 + 1
2 4 3 + 1 (2-1)*3 + 1
3 7 6 + 1 (3-1)*3 + 1
3 10 9 + 1 (4-1)*3 + 1
7.10 Address
This function takes the representation of a cell location like Sheet Name, Row/
Column number, and returns a string containing the address of the concerned
cell.
Syntax for this function is as follows:
= ADDRESS(row_num, column_num, [abs_code], [a1],[sheet_text])

Arguments:
1. row_num: Row number to be used in cell reference.
2. column_num: Column number to be used in cell reference.
3. abs_code: This numeric value specifies the part of the address (row or
column) that should be made absolute. The values are as follows:


1 Row and column both absolute ($A$1)
(or omitted)
2 Row absolute, column relative (A$1)
3 Row relative, column absolute ($A1)
4 Row and column both relative (A1)

4. a1: This is a Boolean value that dictates if the address should be in A1 form
or R1C1 form. The default value is ‘True’ and means that the address should
be in A1 form. We will use the default value.

5. sheet_text: Name of sheet to be included in the address string. If the
worksheet is in some other file, the name of the file has to preceed the sheet
name and enclosed between square brackets ([ ]).
Let us consider some examples:

‘//** Returns $B$1 **//
= ADDRESS(1,2)

‘//** '[Test Book.xls]Sheet4'!$C$1 **//
=(ADDRESS(1,3,1,1,"[Test Book.xls]Sheet4"))

Note in the second example that the workbook name contains a space between
two words. The sheet name can also contain blank spaces, the manner of
entering the formula will remain same.
7.11 Getting values from tables (Index + Match)
Problem:
Often we need to fetch values from one or more tables of data by looking up a
combination of row and column values. The fetched value is then either
displayed or used in other formulas.
Solution:
We will use the Index function with the table as the reference range. The row
and column numbers will be fetched using the Match function on the header row
and the first column of the table.

7.11.1 Sample data- piping engineering


First, a little overview of the sample data that we are going to use. In the field of
process plant engineering a lot of design is done using tabulated data. One such
table is shown in in the figure 7.11a.
This table gives data of pipes of different diameters. The parameters of interest
are the outer diameter, the wall thickness and weight for 1-foot length of a pipe.
The input used is the Nominal Pipe Size (NPS) or Nominal Diameter. It has no
relation with the actual dimensions of the pipe. Another parameter, on which the
dimensions of a pipe depend, is the ‘Schedule’ that roughly determines the wall
thickness for a given NPS.
We will use Index and Match functions to fetch value from a single cell for a
given Schedule value.

Fig 7.11a Sample data



Here’s how the fetching of parameters works:
To pick a value from a table, we start in the first column with a particular NPS
and travel down till we find the required size and we take note of that row. Then
we look at the header row and starting at column 1 we travel to the right till the
header of the column matches the name of the parameter we are looking for.
The cell where the row and column meet is the cell we are interested in.
When executing this activity with formulas we do the following:
1. Use Match function with the first column as the lookup array and the NPS as
the lookup value. The value returned is the row number containing the
specified NPS.

2. Use the Match function with the first row as the lookup array and the
Parameter’s as the lookup value. The value returned is the column number
containing the specified parameter.

3. Use the row and column numbers from steps 1 and 2 in the Index function
where the table is specified as the lookup array.

Fig. 7.11b

In figure 7.11b, cell B2 is where the NPS is entered. Cells B5 to B7 are the
fetched parameters. These cells are on Sheet1 and the pipe table is on sheet3 in
the same workbook.
Cell B5 contains the formula with the INDEX and MATCH functions

=INDEX(Sheet3!$A$1:$E$11,
MATCH(Sheet1!$B$2,Sheet3!$A$1:$A$11,0),
MATCH(Sheet1!$A5,Sheet3!$A$1:$D$1,0))

See how the first MATCH function gets the row number and the second one gets
the column number for the INDEX function.
Notice also that in the second MATCH function the input cell is entered with
Column Absolute reference (missing $ sign in front of 5). This is done so that
the formula can easily be copied to the other two rows without need of changing
the row number.
7.12 The same thing done differently (VLookup +
Match)
In section 7.11 we saw how we can fetch value from a single cell from a table of
data.
We used 3 functions, two of them nested inside another one. Let’s try to reduce
the number of functions used.
We know a function that will take a lookup value, a table of data and a column
number and then will look up a value in the first column and return the value in
the specified column.
Yes, you are right. The function is VLOOKUP. We specify the lookup value and
the data range. The function finds the first row that matches the lookup value
and then travels right to the column that is specified to fetch the value in a cell.
For specifying the column, we will use the match function as done in section
7.11. Accordingly, the formula in cell B5 will now change to:
=VLOOKUP($B$2,Sheet3!$A$1:$E$11,
MATCH(Sheet1!$A5,Sheet3!$A$1:$D$1,0),0)
7.13 A better use for Index + Match
As noted in the section 7.11 when describing piping data, we saw that tabular
data is available for pipes of different schedules. So far we have fetched data
from a table for one schedule. What if we want to give the user a chance to select
the data for a different Schedule?
This is where we can fully utilize the capability of the Index function.
For this to work, we arrange the different tables of different schedules on the
same sheet. Preferably they should be arranged vertically one below the other.
The arrangement of tables for two schedules is shown in figure 7.13a. Notice
two things.
1. The rows and columns in each table are in the same order.
2. Pipe sizes across the tables are the same.

Fig 7.13a Sample data for pipes with multiple schedules

This might mean that in some tables we may need to have rows that have an
entry only in the first column, with blank cells or “na” in other columns. Two
such cases are shown in the bottom table in figure 7.13a where NPS 12 and NPS
20 are not supported for Schedule 80. (This is fictitious data just for the purpose
of illustration, in reality the sizes are supported. Data is also available for pipe
sizes smaller than NPS 8).

Fig 7.13b Layout of cells for fetching pipe details




The figure 7.13b shows the arrangement of cells. The user can enter the
Schedule in cell B1 and the NPS in cell B2, the parameters are fetched in cells
B5 to B7.
Consider the multi-range variation of the formula for INDEX function

=INDEX((Sheet3!$A$1:$E$11,Sheet3!$A$13:$E$22),
MATCH(Sheet1!$B$2,Sheet3!$A$1:$A$11,0),
MATCH(Sheet1!$A5,Sheet3!$A$1:$E$1,0),
MATCH(Sheet1!$B$1,{40,80},0))

The very first argument is the list of different ranges where data is located. The
ranges are separated by comma ( , ) and the entire list is enclosed in brackets.
The second argument is provided by the first MATCH function. It will take the
NPS and locate the row. This will be same for each table since we have same
number of rows in each table.
The third argument is provided by the second MATCH function. It takes the
parameter name and locates the column in the first table. Since all the tables
have the same columns in the same order, the number retrieved from the first
table can be used for all the tables.
The last MATCH function is the interesting part. It contains the values that a
user would enter for Schedule in Cell B1. These values are listed in an array
(enclosed by curly braces “{ }”). The order of the values is same as the order of
ranges in the first argument of the INDEX function (the Schedule 40 table is
listed before Schedule 60 table).
Food for thought:
How is it that this formula works even though the tables are separate?
It works because the table rows have the same values in the first column and the
columns all have the same text in their headers. Also, the order of the rows and
columns is the same for all the tables.
The Match function returns the position of the first matched value. Irrespective
of the table in which we are searching, the position of a given NPS value or
column header is the same in all tables.
Chapter 8
Handy Visual Basic functions
In this chapter we will take a look at some very essential Visual Basic (VB)
functions. A separate chapter is devoted to these functions because they are
frequently required when writing macros irrespective of the type of solution we
are developing.
Notes:
In this chapter, at several places, we have used two common VB functions
(MsgBox and Debug.Print). These are used to display some message to our
users. However, these could be described only towards the end of the chapter in
section 8.4
8.1 Functions for arrays
Let us start by looking at functions that take entire arrays as inputs.
Array:
This function returns a variant array containing the specified values as elements.
The syntax of the function is as follows
Dim arrVar As Variant
arrVar= Array(“John”,“Patrick”,“Mason”)
The values specified can be of any primitive datatype but all of them have to be
of the same type. In our example, the variant array will contain strings. A two
dimensional array can be created as follows:
Dim arrtest As Variant
arrtest = Array(Array(1, 2), Array(3, 4), Array(1, 5))

Notice that ”arrtest” is a 3 (rows) x 2 (columns) array.


IsArray:
This function returns a Boolean value indicating whether a variable is an array. It
is useful in sub and function procedures which are written to operate on array
arguments passed as Variants. In such cases we need to ensure that the argument
passed to the procedure is acutally an Array.
Function arrFunTest2(ByVal strPrefix As String, _
ByRef arrNums As Variant) As String
If Not(IsArray(arrNums)) Then
MsgBox “An Array is required in the function”
arrFunTest2 = “”
Exit Function
End If
‘//** Code of the function **//
End Function


Join:
This function returns a string formed by joining individual strings contained in
an array. Syntax is as follows.
strMain = Join(arrSource, [strDelimiter])
Arguments:
1. arrSource: This is an array of strings that are to be joined into a single string.

2. strDelimiter: An optional argument. This is a string that is placed between
individual strings in the resulting string. If not specified a space (“ “) is used.
Example:
Dim strMainAs String
Dim arrSource(2) as String
arrSource(0) = "John"
arrSource(1) = "Patrick"
arrSource(2) = "Mason"

‘//** Returns “John|Patrick|Mason” **//
strMain = Join(arrSource, “|”)

‘//** Returns “John Patrick Mason” **//
strMain = Join(arrSource)

LBound:
This function returns a long integer value equal to the lower bound (lowest
allowable index) of an array for the specified dimension. Syntax is as follows:
lLb = LBound(arrInput, [lDimension])

Arguments:
1. arrInput: This is an array. It could be a multidimensional array.

2. lDimension: This is an optional argument. It’s a long integer value indicating
the dimension in the array whose lower bound is sought. We can omit this
argument for one-dimensional arrays.
UBound:
This function returns a long integer value equal to the upper bound (highest
allowable subscript) of an array for a specified dimension.
Syntax is as follows (arguments carry the same meaning as for LBound):
lUb = UBound(arrInput, [lDimension])
Example:
Consider the following three dimensional array:
Dim arrInput(1 To 100, 0 To 3, -3 To 4) As Integer

Function Result Function Result


UBound(arrInput, 1) 100 LBound(arrInput, 1) 1
UBound(arrInput, 2) 3 LBound(arrInput, 2) 0
UBound(arrInput, 3) 4 LBound(arrInput, 3) -3

8.1.1 Length of & looping through arrays


The main advantage of knowing the LBound and UBound functions is that we
can write our code without having to worry about the lower and upper bounds of
the arrays we are dealing with. This is mainly useful in two cases:
1. When we have to integrate our code with code written by someone else.
Different programmers may use different bounds based on their style of
programming.
2. When we are writing general purpose functions/sub procedures that can deal
with arrays created by declaration or arrays created by reading values from
Excel ranges.
Looping through an array:
The code below is for a general purpose function that looks at all members of a
one-dimensional array to find an exact match for a string. The index of the
element that matches is returned. If an exact match is not found, we return a
number one less than the lower bound of the array.
Function ExactMatchPosition(ByRef arrTemp As Variant, _
ByVal strToFind As String) As Long
‘//** Long integer to hold the index position of the string that is found **//
Dim lFoundIndex As Long
‘//** Set the default return position in case the string being searched is not found **//
lFoundIndex = LBound(arrinput) - 1
For i = LBound(arrTemp) To UBound(arrTemp) Step 1
If StrComp(arrTemp(i), strToFind) = 0 Then

‘//** Match found, set return value to array index **//
lFoundIndex = i

‘Since we found one instance, look no further, exit the loop


Exit For
End If
Next i
‘By this time, we have either the default value or the position of the exact match
‘Return the value that we have
ExactMatchPosition = lFoundIndex
End Function




Length of an array:
Length of an array along any dimension is given by: Ubound – Lbound + 1

Hence considering the array arrInput described in the “UBOUND” function in
section 8.1, the following table shows the length along various dimensions
Dimension Length along this dimension
1 100 – 1 + 1 = 100
2 3 – 0 + 1 = 4
3 4 – (-3) + 1 = 8

8.1.2 Checking dimensions of an array


Often we would have to write a function or sub procedure to process a 2-
Dimensional array. However, the array itself is passed as a Variant argument. In
that case, the calling function can pass any type of variable or a one dimensional
array as an argument and cause an error.
However, inside the procedure we can check if the passed argument is an array
of the correct dimensions. This is done using the IsArray function.
This check should be done before we use the LBound or UBound functions
because trying to check a bound on a dimension that does not exist will cause an
error.
Function arrFunTest2(ByVal strPrefix As String, _
ByRef arrNums As Variant) As String
If (IsArray(arrNums)) Then
‘//Its an array so it will have atleast one dimension. Get the element atthe
highest ‘//index in the first dimension
Dim arrVar as Variant
arrVar = arrNums(UBound(arrNums,1))
‘//This element has to be an array
If Not(IsArray(arrVar)) Then
‘//The passed argument is not a 2-Dimensional array
End If
Else
‘//The passed argument is not even a 1-Dimensional array,
End If
End Function
8.2 Functions for strings
InStr:
As the name (“in string”) suggests, this function finds the location of one string
(the target string, strTarget) within another string (the source string, strSource).
The value returned can be used in an IF test to execute further statements.
The return value is a long integer containing the position of the first occurrence
of the target string. If valid arguments are specified, then a return value greater
than 0 indicates that the target string is present in the source string.
Syntax for this function is as follows:
lPos = InStr([start, ]strSource, strTraget[, compareType])

Arguments:
1. start: This is a numeric value that indicates the position in source string at
which the search for target string will begin. It is an optional argument.

The default value of ‘1’ is used if we omit a value for this argument. We
cannot put a NULL value for this argument. In our examples we will always
use the value of 1.If the last argument compareType is specified, then it is
mandatory to put a value for start.

2. strSource: This is the string in which we want to find the target string. This
argument is required.

3. strTarget: The string that we are trying to find in the source string. This
argument is also required.

4. compareType: This argument specifies the method of comparison to be used
while searching. This is an optional argument.

If omitted, the value specified by the ‘Option Compare’ statement is used.
The ‘Option Compare’ statement is one of the first statements in a module,
written before any procedure or declaration statements.

We will not depend on the ‘Option Compare’ statement and will always use
the value of ‘1’ that is ‘Text Comparison’. Other comparison methods are
‘Binary Comparison’ (0) and ‘Database Comparison’ (2). We will not use
these setting either because better methods are available when dealing with
binary files and databases.

However, if we want the search to be case sensitive then we should use
‘Binary Comparison’. The examples will clarify this concept. Also, if an
Option compare statement is not present AND the argument is omitted, then
the Binary comparison is used.
The following table contains a list of the different values that the function would
return under different conditions:
Condition Return value
Source string is Null Null
Source string is of zero length 0
Target string is Null Null
Target string is of zero length Start
Target string is not found from the
0
starting position
Start > length(Target String) 0
Target string is found inside Source Position at which target string begins
string in source string

Let’s take some examples now. Consider the source string
strSource = “Hello, this is a HELLO string”

Following table shows the values returned for different values of the start and
’compareType’ parameters as well as different values of the Target string.
Function Return value Case Sensitive
InStr(1,strSource,”Hello”,1) 1 N
InStr(1,strSource,”HELLO”,1) 1 N
InStr(8,strSource,”hello”,1) 18 N
InStr(1,strSource,”what”,1) 0 N
InStr(1,strSource,”Hello”,0) 1 Y
InStr(8,strSource,”hello”,0) 0 Y
InStr(1,strSource,”what”,0) 0 Y
InStr(1,strSource,”HELLO”,0) 18 Y

Common use of this function:
InStr is most useful in finding out if a certain target string is present in a block of
text when we don’t know the exact position of the target string.
For example, for testing if the subject of an email has a certain word.In the
following code, strSubjectis a string containing subject of an email.
If InStr(1,strSubject,”Sales”,1) > 0 Then
‘//** Found the email we were looking for, process further **//
End If

Split:
This function is the opposite of Join function described in section 8.1. It takes as
argument an input string that has a specific character at certain locations. It then
breaks the string wherever the character is found, puts the pieces in a one-
dimensional array and returns the array. This array can be captured in a variable
of Variant data type.
The syntax for the function is as follows:
Split(strSource[, Delimiter[, Limit[,compareType]]])

Arguments:
1. strSource: String containing substrings and delimiters. If expression is a
zero-length string(""), Split returns an empty array, that is, an array with no
elements and no data. This is a required argument.

2. Delimiter: An optional argument. It is a string used to identify the positions
at which strSourcewill be broken. If omitted, the space character (" ") is
assumed to be the delimiter. If delimiter is a zero-length string (“”), a single-
element array containing the entirestrSourceis returned.

3. Limit:Specifies thenumber of substrings to be returned. This is an optional
argument. If omitted a default value of -1 is taken,meaning that all substrings
are returned. Value, if specified, must be a positive number.

If the value specified is more than the number of pieces that are possible, the
returned array will contain only the maximum possible number of strings.

If a value (say n) is specified, our macro will start breaking the string starting
at the leftmost (first) occurrence of Delimiter. This continues till (n-1) pieces
of strings are available. The remaining part of the original string is then
placed in the last element of the returned array. This concept will be clarified
by examples.

4. compareType:This is an optional argument. Its meaning is same as that
specified for the InStr function described earlier in this section.

Most of the times we will split strings using non-alphabetical characters.
Some examples are @ , / , ; , | , ,(comma) , $ , and so on. However,
sometimes we may want to use strings containing alphabets. In such cases we
may want to use Binary comparison (value 0). The examples will make this
point clear.

Special notes:
1. The lower-bound of the returned array is always 0.
2. The strings in the returned array will contain any non-alphabetical character
or white spaces that are present in the original string.

Let’s see some examples. In the examples, strSource is the string we will split.

strSource = "Hello Hi/ goodbye hi bye HI buddy"

Function Returned array


1 Split(strSource, "hi", , 1) (“Hello“, ”/ goodbye “, ” bye “, ”
buddy”)
2 Split(strSource, "hi", 2, 1) (“Hello “, “/ goodbye hi bye HI
buddy”)
3 Split(strSource, "HI", , 1) (“Hello“, ”/ goodbye “, ” bye “, ”
buddy”)
4 Split(strSource, "HI", , 0) (“Hello Hi/ goodbye hi bye “, “
buddy”)

In the “Returned array” column, a comma (,) is used to separate the different
array elements. Notice carefully the blank spaces after ‘Hello’, and on both sides
of ‘bye’ that are returned in the array elements. Also note the effect of using
binary comparison in row 4.

Common use of this function:
One frequent use of this function is to separate a file path into drive letters and
file names.
Dim arrStr As Variant
strPath = "C:\Reports\InputFiles\SalesReport.xlsx"
arrStr = Split(strPath,”\”)

Array Element
arrStr(0) First element. The drive name “C:”
arrStr(UBound(arrStr)) Last element. The file name “SalesReport.xlsx”

StrComp:
This function is used to check if two strings are exactly equal. If the strings are
equal the function returns 0 else, it returns 1 or -1.
The syntax for the function is as follows:
StrComp(str1, str2[,compareType])

Arguments:
1. str1, str2: These are the two strings to be compared. If any of these strings is
omitted, the function returns NULL.

2. compareType: This is an optional argument. Its meaning is same as that
specified for the InStr function described earlier in this section. We will most
often need only ‘Text Comparison’ (value 1). Sometimes we may wish to do
a case-sensitive comparison and in that case we should use ‘Binary
Comparison’ (value 0).




Return Condition
Value
0 str1 is exactly equal to str2
-1 str1 is less than str2
1 str1 is greater than str2
NULL Either str1 or str2 is NULL (omitted or zero length
string)

In case of Binary comparison, the return value is 0 only when both strings have
same characters at the same positions and the case(upper/lower) of the characters
match in each position.
How one string is greater than or less than the other depends on a combination of
things like the position of the characters and whether they are in upper or lower
case. A thorough discussion is out of the scope of this book as it requires
knowledge of how characters are represented in a computer.

Food for thought:


Computers don’t understand alphabets like humans do. So every alphabet or
character is represented inside a computer with a numeric code called the
ASCII code of the character.
ASCII stands for American Standard Code for Information Interchange.

Here are the codes for some of the characters
Character Code
Letters A to Z 65 to 90
Letters a to z 97 to 122
Digits 0 to 9 48 to 57
Space “ “ 32
Clearly, lower case alphabets have higher code values than upper case
alphabets and will play a role in deciding if the string containing them is
greater or less.








The following table shows some examples that should clarify the concepts.
Function Return Remarks
Value
StrComp("HELLO", "hello", 0) -1 Binary comparison.
StrComp("HELLO", "hello", 1) 0 Text comparison
StrComp("HELLO", "HLELO", -1 Note the change in the second
1) string. Position matters
StrComp("HeLLO", "HELLO", 1 Note the change in the first
0) string. Case matters

The best use of this function is to check if two strings are equal while keeping in
mind the comparison type we are using. Our macro should not depend on which
string is greater or less than the other.
Val
This function returns a numeric value made of the numbers contained in a string.
It is useful for converting numbers, that are provided or entered as text, into
actual numbers. We can assign the returned value to a numeric variable. The
syntax is as follows
Dim retval As Double
Retval = Val(strNum)

The argument ’strNum’ is the string containing the number we want to extract.
There are two points to be noted when using this function:
1. The function stops reading the string at the first character it can't recognize as
part of a number.
2. Blanks, tabs, and linefeed characters are removed from the argument.

strNum Value returned


“ 245 East street” 245
“ 2 65 7 “ 2657
“34 and 57” 34

Replace
This function takes one string as argument and returns another string in which
specific characters have been replaced with another set of characters for a
specified number of occurrences. The syntax is as follows:
Replace(expression, find, replace[,start[,count[,compareType]]])


Arguments:
1. expression: this is a string argument within which we want to replace specific
characters. If a zero length string ( “”) is specified, then the function returns a
zero length string. If this argument is NULL, an error occurs.

2. find: this is the character (or group of characters) that will be replaced. If a
zero length string ( “”) is specified, we get back a copy of ’expression’.

3. replace: the new character(s) that will replace the character(s) specified in
find. If a zero length string ( “”) is specified, we get back a copy of
’expression’ in which all ocurrences of ’find’ are removed.

4. start: this is Position within ’expression’ where substring search is to begin. If
omitted, 1 is assumed. No replacement happens at positions ahead of this one.

5. count: this is number of substring substitutions to perform. If omitted, the
default is to make all possible substitutions. If the value specified is 0, we get
back a copy of ’expression’.

6. compareType: same as that described for InStr function earlier. We will use
the text comparison (1).

Arguments 1 to 3 are required arguments and all others are optional and can be
safely omitted. For the code example shown below, the variable ’strOutput’ will
end up with the value “John Patrick Mason”

Dim strInput, strOutput As String
strInput = “John|Patrick|Mason”
strOutput =Replace(strInput,“|”,“ “,,,1)

Other string Functions:


Along with the functions already described in this section, there are 3 more
fuctions named Left, Right and Mid. These function exactly as the functions
available on a worksheet except that instead of displaying the result in a cell,
they return a string value.
8.3 Functions for dates
Two important date related functions, Date and DateSerial, have been described
in section 2.3 “Specifying dates in macros”. In this section, we will see some
functions that allow us to conduct arithmetic operations on dates.
DateDiff
Returns aLong integer specifying the number of time intervals between two
specified dates. The syntax is as follows
DateDiff(interval,date1,date2[, _
firstdayofweek[, firstweekofyear]])
Arguments:
1. interval: this is a string argument and specifies the interval in which we want
to calculate the difference between dates.

interval Value returned
yyyy Number of complete years between two dates
q Number of complete quarters between two dates
m Number of complete months between two dates
d Number of complete days between two dates
ww Number of full calendar weeks between two
dates
w Number of weeks counted from weekday to
weekday

2. date1 and date2: theseare arguments of the Date datatype. They are the two
dates between which we want to find the difference. If ’date1’ is a later date
than date2 then the result is a negative number.

3. Firstdayofweek: this argument is applicable when we are formatting dates. A
value of 1, the default, specifies Sunday as the first day. A value of 2 means
the program takes Monday as the first day. We can go on till 7 where Saturday
is treated as the first day of any week.

4. Firstweekofyear: this argument is applicable when we are formatting dates. It
specifies the week that should be considered as the first week for the year.
This argument becomes useful for analyzing time clocking and financial
systems when the calendars begin with the first full week of the year. The first
partial week is treated as part of previous year. The values are as shown in the
following table

Value Description.
1 Default. Whichever week has Jan 1 is the
first week
2 The week that has atleast 4 days of the
current year is treated as the first week of
the current year
3 Star with the first full week of the year.

The following sub shows an example of using this function. The output
generated is in the comments.



Sub GetDateDifference()
Dim dt1, dt2 As Date

‘// ** Thursday **//


dt1 = DateSerial(2016, 4, 14)
‘//** Sunday **//
dt2 = DateSerial(2016, 4, 24)
‘//** output – 2 **//
Debug.Print DateDiff("ww", dt1, dt2)
‘//** output – 1 **//
Debug.Print DateDiff("w", dt1, dt2)

End Sub

DateAdd
This function is used to add a specified number of intervals to a given date. The
result of addition is returned. Here is the syntax
DateAdd(interval, number, date)
Arguments:
1. interval: this argument has the same meaning as specified for the DateDiff
function earlier. Hence we can add/subtract days, weeks, months, quarters and
even years.

2. date: this is the date to which we want to perform the addition.

3. number: this argument is a long integer and specifies the number of intervals
to be added to the specified date. If this argument is positive we get a future
date, if negative we get a past date.
As an example, in the code below, ’dt2’ will have the value 28-04-2016
Sub TwoWeeksInFuture()
Dim dt1,dt2 As Date
dt1 = DateSerial(2016, 4, 14)
dt2 = DateAdd("ww", 2, dt1)
End Sub

DatePart
Returns an integer containing the specified part of a given date. For example, it
tells us which month or week of the year the specified date falls in. The syntax is
DatePart(interval, date[,firstdayofweek[, firstweekofyear]])

The arguments have the same meaning as specified for the DateDiff function
earlier.

Sub GetQuarter()
Dim dt1 As Date
Dim qNum As Integer
dt1 = DateSerial(2016, 4, 14)
qNum = DatePart(“q”, dt)
MsgBox “Date is in quarter “ & qNum& “of this year”
End Sub

The sub procedure shown above will produce a message box as shown in the
following figure
8.4 Other functions
Format function
This function takes a date or number (or a date or numeric expression) and
returns a string containing the date or number formatted ina manner that we
spedify. This is very useful for printing dates or numbers in specific formats in
Word or PowerPoint reports or for storing values in a database.
Here is the Syntax:
Format(expression[,format[,firstdayofweek[,firstweekofyear]]])

Arguments:
1. expression: this is the numeric or date expression that we want to format
2. format: this is a string argument and holds the formatting that we want to
apply to the expression. We will see the different format strings with
examples.

The arguments ’firstdayofweek’ and ’firstweekofyear’ carry the same meaning as
described for the DateDiff function in section 8.3. These are optional arguments,
even when we are formatting dates.

When we are formatting dates, the following symbols have to be used in the
string of the format argument:
Symbol Used to obtain
d Day in single digit
dd Day in double digits
ddd Abbreviated name of the day (Mon,
Tue….)
dddd Full name of the day
M Month in single digit
Mm Month in double digits
Mmm Abbreviated name of the month
Mmmm Full name of the month
Yy Year in two digits
Yyyy Year in four ditgits
Ww Week number in two digits

Let’s look at some examples. Consider the following variables
Dim dblVal As Double
Dim dblFraction As Double
Dim dt As Date
dblFraction = 0.254786
dblVal = 12547835.234578
dt = DateSerial(2016, 6, 7)


Function Output
Format(dt, "ww") 24
Format(dt, "yyyy") 2016
Format(dt, "ddd, dd mmmm yyyy") Tue, 07 June 2016
Format(dt, "dddd, dd mmm yyyy") Tuesday, 07 Jun 2016
Format(dt, "dd,m,yy") 07,6,16
Format(dt, "dd.mm.yy") 07.06.16
Format(dt, "d!mm!yy") 7!06!16
Format(dblFraction, "0.00%") 25.48%
Format(dblVal, "0,000.00%") 1,254,783,523.46%
Format(dblVal, "0,000.00") 12,547,835.23
Format(dblVal, "0.000") 12547835.235


Debug.Print
This function is actually a method of a special type of object called the “Debug”
object. This object is available when we are writing our code in the Visual Basic
Editor.
The Print method takes a string argument and prints it in the “Immediate”
window thereby helping us to see the values of variables and output of functions
before we use further in the program.
This provides a kind of initial check so we can ensure that variables have the
correct values and that the correct processing is being done. This helps to avoid
errors.
For example:
Debug.Print "Week number " &_
Format(dt, "ww") & " of the year " & Format(dt, "yyyy")

Prints the following line in the Immediate window:
Week number 24 of the year 2016

MsgBox
Displays a message in a dialog box, waits for the user to click a button, and
returns an Integer indicating which button the user clicked. We don’t have to use
the return value we can just ignore it.
MsgBox(prompt[, buttons] [, title] [, helpfile, context])

Arguments:
1. prompt: this is a string argument and is the actual message displayed to the
user.
2. buttons: this is an integer specifying the combination of buttons to be
displayed. It is an optional argument. We can enter the integer value or the
related named constant that is predefined in Visual Basic.

Constant Value Display buttons
vbOKOnly 0 OK button only.
vbOKCancel 1 OK and Cancel buttons.
vbAbortRetryIgnore 2 Abort, Retry, and Ignore
buttons.
vbYesNoCancel 3 Yes, No, and Cancel buttons.
vbYesNo 4 Yes and No buttons.
vbRetryCancel 5 Retry and Cancel buttons.

3. title: this is a string containing the text to be displayed in the title bar of the
message box window. This is an optional argument.

The arguments ’helpfile’ and ’context’ are used in Visual Basic applications that
have help files associated with them. We will not be using these arguments.
The values returned by the function are integers. We can compare them with
integer values or with predefined Visual Basic constants listed in the table that
follows
Constant Value User pressed
vbOK 1 OK
vbCancel 2 Cancel
vbAbort 3 Abort
vbRetry 4 Retry
vbIgnore 5 Ignore
vbYes 6 Yes
vbNo 7 No

The code below shows how we can use the MsgBox function and then take
different actions based on the button pressed by the user. The Debug.Print
statements can be replaced by any valid lines of code or function calls.
Sub msgbxtest()
Dim retval As Variant
retval = MsgBox("press any one button", _
vbYesNoCancel, “Button Test”)

Select Case retval
Case vbCancel:
Debug.Print "You pressed Cancel"
Case vbYes:
Debug.Print "You pressed Yes"
Case vbNo:
Debug.Print "You pressed No"
End Select
End Sub



Figure 8.4a shows the output of the sub and the different parts of the message
box as they appear on the screen.

Figure 8.4a Message box and its parts

Most of the times in our macros we would just have to display a message with an
OK button to display a situation or warning to the user and then exit the
sub/function. In that case we can just use the prompt argument and ignore all
other arguments.
IsNumeric
Returns a Boolean value indicating whether an expression in the argument can
be evaluated as a number. The syntax is as follows:
IsNumeric(expression)

The argument ’expression’ is a required argument and can be of Variant


datatype. It can contain a numeric expression or string expression. Consider the
following code example:

Dim var1 As Variant
Dim str1 As String
var1 = "41 E street"
str1 = " 125.36 "
‘//** prints FALSE **//
Debug.Print IsNumeric(var1)
‘//** prints TRUE **//
Debug.Print IsNumeric(str1)


Notice how the variable ’str1’ has blank spaces but can still be evaluated as a
number
Chapter 9
Excel Tables
Tables are a great way to get data organized on a spreadsheet. Excel adds several
features to the range of data once we mark that range as a table. In this chapter
we will start with basics of creating and working with Tables manually on a
worksheet. Later on we will see how we can integrate the tables on worksheets
with our macros.
9.1 Creating a Table on the worksheet
For creating a table (refer to figure 9.1a),
1. We click anywhere inside the range containing our data.
2. On the “Insert” tab to reveal the Insert ribbon, click on “Table”.
3. Excel shows the range from which the table will be created. We can include
additional rows or columns.
4. Always have a header row and, in the “Create Table” dialog box, always
check the box that says “My Table has Headers”. This has many advantages
that we will highlight going forward.

Fig 9.1a (Left) Inserting a table, (Right) the ‘Create Table’ dialog box

Advantages of using tables:
1. The top row is marked as a header row (if the relevant option is selected).
This row is then frozen and does not scroll with datatherebygiving better
readability.
2. Filter drop downs are automatically added to all the columns.
3. The table expands automatically whenever an additional row or column is
added immediately adjoining the existing table.
4. If a formula is inserted in any cell in any column of the table, Excel will fill
that formula in the entire column automatically.
5. We can refer to the table and table columns by their names in Excel formulas
and, in VBA.
6. Most important, when applying Autofilter or when sorting a table, we don’t
need to remember or work out the column positions like we did in section
earlier sections. We can just use the column names.
Features 3 and 4 listed above may not be available by default. In case you notice
that your tables are not expanding automatically, you can enable the option in
Excel.
For doing this, go to “File” menu and click on “Options” on the left hand pane.
In the dialog box that comes up, proceed as shown in the Fig 9.1b.

Fig 9.1b Settings to make tables auto expand and auto calculate
9.2 Using Table and column names in Formulas
When a table is created, Excel automatically names the table and its columns
(based on the column headers). The default names are like Table1, Table2 and so
on.
We should always rename a table to a meaningful name representing the data
therein. Let us see how we can change the name of a table. Refer to Fig 9.2. The
number of steps correspond to the numbers marked in the figure.
1. Click on any cell inside the table.
2. The design tab will now appear in the main menu bar. Click on the Design
tab.
3. Notice on the far left, there is a field that accepts the table name. Enter a
suitable name and click enter.

Fig 9.2 Steps to change name of a table and for making a Pivot table.

When using the names of Table and columns in a formula the format would be
TableName[ColumnName]

For purpose of this section, consider a table holding sales data. The table name is
‘Sales’ having one column with header ‘Units Sold’ and another one with ‘Unit
Price’.

9.2.1 In Mathematical functions


The total sales revenue can be found by entering the formula in any cell in the
workbook:
=SumProduct(Sales[Units Sold], Sales[Unit Price])
The good thing about tables is that they can be accessed even from some other
sheet using their names and we don’t have to prefix the Sheet name to the table
name.
For example, if the Sales table is on sheet 1 and on sheet 2 we want to calculate
the average unit price of our products. We can use the following formula and it
will work.
=Average(Sales[Unit Price])

9.2.2 In Lookup functions


Now let us try and use tables in VLOOKUP. Recall the syntax for the function:
VLOOKUP(lookup_value, table_array,col_index_num,[range_lookup])

For simplicity we will set the Range lookup value to False.


lookup_value : is either a cell reference or a value that we type in by hand.
table_array : can be replaced by the name of a Table. Excel will search for the
lookup value in the first column of the table.
col_index_num : is a numeric value usually typed by hand. But as we saw in
section 7.11, we can use the MATCH function to find the position of a column
with a particular column name in the header row.
But obviously, for using MATCH we need to know the location of the header
row and specify it in the formula like this “$1:$1”.
However, when working with Tables, we get one more advantage. We can use
the #Headers attribute of a table to get the topmost row. We can then pass that to
the MATCH function. This way we don’t have to worry about the location of the
data or the header row when writing our formula.
Let us consider an example. For our Sales table, the vlookup formula would be.
=VLOOKUP(A3,Sales,Match(“Units Sold”,Sales[#Headers],0),False)

So that is another advantage of creating tables on our worksheets.


9.3 Creating Pivot Table with a Table as data source
This is quite straightforward.
Click anywhere inside the table. Then go to the ‘Design’ tab in the ribbon. Click
on “Summarize with Pivot table”. (Refer back to Fig 9.2 and see marker 4).
Follow the instructions on the screen. That’s it. We can create our PivotTable as
usual.
9.4 Creating a Chart with a Table as a data source
Consider the table shown in figure 9.4a. its name is “Sales”

Fig 9.4a Sample Table

To create a chart based on this table, go to the sheet where the chart is required
(this can be different from the sheet that contains the table). Then proceed as
follows:
In the “Insert” ribbon, select the type of chart required. Excel will insert a chart.
This chart may not contain the series that we want. We will set that
Right click on the chart and select “Select Data”. This brings up the “Select Data
Source” dialog as shown in figure 9.4b.
On the right, there is a list named “Legend Entries”. Marked by number 1 in
figure 9.4b. If there is a series there that we don’t want, select it and click on
“Remove”.

Fig 9.4bSelect Data Source dialog box

To add a series, click on the “Add” button. To bring up the “Edit Series” dialog
box as shown in figure 9.4c. In the “Series name” box, enter a suitable name for
the series. Now comes the important part.
In the “Series values” box, we need to enter the name of the column from which
values will be taken to create the chart.

Fig 9.4c Edit Series dialog box

The column name has to be entered in the format


<Table Name>[Column Name]
On the right side in the “Select Data Source” dialog box, there is a list named
“Horizontal Axis Labels”. Marked by number 2 in figure 9.4b. Click on the
“Edit” button for this list to bring up the “Axis Labels” dialog box as shown in
figure 9.4d.
In the box provided, enter the name of the column from which the horizontal
axis labels for the chart will be taken.

Fig 9.4d Axis Labels dialog box



The column name has to be entered in the format
<Table Name>[Column Name]

This creates a chart as shown in figure 9.4e which is based on table in the figure
9.4a columsn “Part Number” and “Units”

Fig 9.4e Chart created using columns in a Table


9.5 Using Table and column names in VBA
In VBA, each Table of data is known as a “ListObject” and is part of the
worksheet where the table is created. All the tables on a worksheet are contained
in a “ListObjects” collection.
In this section we will review the most commonly used objects when working
with Tables in VBA.
ListObjects:
This is a collection of all the Tables created on a worksheet. The syntax for
obtaining this collection is as follows:
Wks.ListObjects

Wks : Is a Worksheet object that refers to an existing worksheet.


Properties
1. Count: This is a long integer giving the total number of tables contained in a
worksheet.

2. Item: This represents a particular Table on a worksheet. There are two ways
to access a table. The object returned is a ListObject that we describe next.
The first syntax is as follows:
Dim lo As ListObject
Set lo = Wks.ListObjects.Item(n)

Here n is a long integer representing the index of the table in a collection.


Obviously, this method is not very intuitive. It is useful only if there is a
single table on the worksheet.
A better method is to use the name of the concerned table as shown in the
following syntax. Refer to figure 9.4a
Set lo = Wks.ListObjects.Item(“Sales”)
ListObject:
This object represents a specific Table created on a worksheet. It is obtained
from the ListObjects collection as described in the previous section.
Properties
1. Range: This represents the range of cells on the worksheet occupied by the
entire table. All properties and methods of the Excel Range object are
applicable.

2. DataBodyRange: This represents only the range of cells on the worksheet
occupied by the data in the table. That means the header row is excluded.
This is useful if we are trying to combine multiple tables into a single table.
In that case we might want to exclude the header row when we are appending
one table below another.

3. ListColumns: This is a collection of columns of a Table. Each column is
represented by a ListColumn object. A particular column can be obtained
using the Item property.
For example, in case of the Sales table, the column for Unit Price can be
obtained as follows (Refer to figure 9.4a):
Dim lc as ListColumn

Dim lo as ListObject
Set lo = Wks.ListObjects.Item(“Sales”)

Set lc = lo.ListColumns.Item(“Units”)

An important method of this collection is the ‘Add’ method that allows us to
add extra columns to a table. We will see the use of this method via an
example towards the end of this section.

4. Name & DisplayName: These are names of the Table as they are set in the
“Design” tab when the Table is created. We will not be using them much as
they are required to obtain the objects in the first place.

5. HeaderRowRange: This is a range object holding the header row of the
table. It is useful if we want to do a quick check if a particular word is
present in the header row.

ListColumn:
This object represents a specific column of a table. It is obtained from the
ListColumns collection as described under “ListObject” object.
Properties
1. Range: This represents the range of cells on the worksheet occupied by the
entire column. All properties and methods of the Excel Range object are
applicable. It includes the column header.

2. DataBodyRange: This represents only the range of cells on the worksheet
occupied by the data in the column. That means the header row is excluded.
Application.WorksheetFunction.Sum _ (lo.ListColumns("Units").DataBodyRange)

The code above shows that the DataBodyRange can be treated like any other
range object.
3. Index: This is an integer value representing the position of the column in the
table.

4. Name: This is a string value and is the name of a particular column. This
string when set will appear as the column header.
Methods
1. Delete: This method will remove a the table column represented by the
ListColumn object. Here is the syntax:
Set lo = Wks.ListObjects.Item(“Sales”)

lo.ListColumns.Item(“Units”).Delete

The effect is same as deleting the column from the worksheet. Columns from
the right are moved leftwards to occupy the deleted column.
The code below will add a new column at the end of the table and fill it with a
formula
Dim lo As ListObject

Dim lc As ListColumn

Set lo = Worksheets("Sheet3").ListObjects("Sales")

Set lc = lo.ListColumns.Add

lc.Name = "NewCol"

lc.DataBodyRange.Cells(1, 1).Formula = _
"=Left(Sales[[#This Row],[Part Number]],3)"

Notice that the formula is filled in the first cell of the column. The table will fill
the formula in the entire column.
The formula is to fetch the first 3 characters from the “Part Number” column
from the table in figure 9.4a.
Notice the use of ”#This Row” to refer to the row in which the formula is
entered.
Chapter 10
Dynamic Named Ranges
We know that we can very easily select a fixed range of cells on a worksheet and
give it a name. We can then use the name to refer to that group of cells in our
formulas.
In this chapter we will see how we can take the concept of Name a bit further
and make the names respond to changes made on a worksheet. Along the way
we will see some principles for developing spreadsheet based solutions.
This chapter is a rather long one. All efforts have been made to keep the writing
light so as not to bore the reader, but the main focus is on getting straight to
point.and avoiding lengthy narratives. Reader is requested to please bear with
the length of the chapter. Let’s get started.
Just to refresh our memory, let’s see how we can create a named range. Use Fig
10a as reference.

Fig 10a Creating a named range


1. Select the cells that have to be included in the range. We would select the
range B4:G12.
2. In the “Name Box” (marked as (1) in Fig 10a), type in the desired name.
Here we can see the name as “TableProd”.
The name will now be listed in the “Name Manager” dialog box. We can access
this dialog by going to the “FORMULAS” (marked as (2) in Fig 10a) tab on the
ribbon and then clicking on the “Name Manager” icon (marked as (3) in Fig
10a).
But, Names can do much more than just refer to a group of cells. The real power
of Names lies in their ability to hold formulas. Formulas that return not just
values but an entire array of cells.
In the upcoming sections we will look at applications where we can create a
range of cells (mostly a single row or column) that can then be used for purposes
like calculating sums and moving averages as well as in self expanding chart
series.
10.1 Rules for choosing names for a range
When creating a Name we need to take care of the following:
1. The name should always start with a letter or an underscore ( _ ). The
remaining characters can be letters, numbers, underscores or periods ( . ). No
other characters are allowed.

2. The name should not have a space in it. If we want to separate two words we
should use an underscore ( _ ) or a period ( . ).

3. Avoid single character names. If names with only one character have to be
used, avoid using the characters “R”,”r”,”C” and “c” since these are used to
refer to cells in a different referencing style.

4. Avoid names that look like cell addresses like AC100 or R$50 and so on.

5. Upper and lower case names are treated as same. For example, if we create a
name “MONTHLY_SALES” and then create “monthly_sales” or
“Monthly_Sales”, Excel will display an error message saying that a duplicate
name is being created.

6. Length of a name should be less than 255 characters.
10.2 Self-Expanding Named Ranges on Worksheet
We have already seen functions that take some parameters and return a Range.
The most useful function of this type is the Offset function that we learnt in
section 7.9. Let’s recall its syntax,
=OFFSET(ref.Cell, rows, cols, [height], [width])

If we have a fixed reference cell and know the rows and column offset of the
start of our range, we have a starting point.
Now all we have to do is to make our height and width vary based on the data
that is put into our range and we can have a reference to the whole range.
What we need is a function that would calculate for us the total number of rows
and/or columns that have been filled. And we have already seen this function.
You’re are right, we can use the CountA function that we saw in section 7.5.
Let’s recall its syntax
= (CountA(rngReference) – 1)

rngReference would mean a row (called the reference row) or a column (called
the reference column) which will have data or, more appropriately, data-labels in
all of its cells. We will see the details soon.
But wait, some would say. Doesn’t that mean that the limitations of CountA
come into play? Because for CountA to work the following should hold:
1. There should not be any blank cell in between two non-empty cells, at least
in the reference row or in the reference column.

2. There should not be any content after the extreme right column or below the
last row in the reference column, otherwise that too would get counted.
Well, it’s true that layout of the data matters when we are creating dynamic
ranges. But it pales in comparison to the functionality we gain once the ranges
are created.
Another question. Why do we need a ‘reference’ row or column? Why not just
apply CountA formula to the actual range containing the data?
To get an answer, consider the layout of data shown in Fig 10.2a
Fig 10.2a Sample data for creating a dynamic range with one row

Every week we add a new column for another week. Suppose that we want to
create a dynamic range for ‘Revenues’. But, as we can see from the figure, we
don’t have data for some of the weeks. May be the value is 0 and we just didn’t
enter it.
Let’s say we used the following:
= CountA(6:6)-1

This would give us a value of ‘8’. So starting at column C and counting 8 cells
would give us a range only till column J, so Weeks 9 and10 get missed out.
Instead, let’s try using the following formula.
= CountA(5:5)-1
Basically this says:
‘Count all non-blank cells and remove one cell that holds the row label
Revenues’
This would give us a range exactly 10 columns wide which is what we want. The
width of our range is hence 10. The range in this case is 1 row, so the height of
the range is 1.
Now let’s create our range for ‘Revenues’ row. We call our trusted friend the
‘Offset’ function.
=OFFSET(ref.Cell, rows, cols, height, width)

ref.Cell : The reference cell would be the top left corner of our data. Cell B5.
Rows : Revenues range will lie one row below the reference cell. So the rows
offset is 1.
Cols : Revenues range will start one column to the left of the reference cell. So
the columns offset is 1.
Height : Our range consists of only one row, so it’s height is 1.
Width : This is the fun part. We want our width to be calculated. So, we enter the
CountA formula for width instead of a numeric value.
Here’s what our final formula would look like
=OFFSET(Sheet7!$B$5,1,1,1, CountA(5:5)-1)

But wait, we can’t just enter this formula into a cell. Remember that offset
returns a range, if we use this on a worksheet, we would have to enter it as an
array formula across a range of cells and that would serve only a limited
purpose. Instead, we will save this formula as a Name. That’s right. In the next
few sections we learn how we can use this technique to get references to ranges
having a single row/column as well as an entire table.

10.2.1 Dynamic range with a single row


Let’s work with the data shown in Fig 10.2.1a and create a named range for the
‘Revenues’ data. Here is how we proceed:
1. On the “Formulas” ribbon, click “Name Manager”. This brings up the Name
Manager dialog box shown in figure 10.2.1a

Assume that we are creating a spreadsheet for tracking weekly revenues and
expenses for our firm.
We will start with Revenue and Expense and then build more as we go
along.


Fig 10.2.1a The Name manager dialog box
2. In the dialog Name Manager box, click the button marked “New”. This will
bring up the “New Name” dialog box

The New Name dialog box

Very Important:
Ensure that “Workbook” is selected in the dropdown named “Scope”.
This will ensure that the range that the Name refers to can be used in formulas
inserted on some other sheet
3. In the field labelled as ‘Name’, we type in the name we want to use for the
range. In our case, it will be ‘Revenues’

4. By default, in the ‘Refers To’ field we see the address of the currently
selected cell.

5. We click in that box, and start typing the Offset formula.

6. Very important, when it comes to entering the reference cell and reference
row, we don’t type, we select the concerned cell and row. Why? The reason
will be clear in a minute.

7. Click on ‘Ok’. The new Name will appear in the Name manager box with list
of other Names.
As we can see, Excel inserts the sheet name and ‘$’ signs into the formula. Leave
them as they are, don’t delete them. Why? Let’s see.

The dialog box in figure 10.2.1b is the ‘Edit Name’ dialog box and is used to
make changes to an already existing name.
Open the Name Manager as described in step 1. Select any name from the
list that needs to be changed. Click “Edit”

Fig 10.2.1b A dynamic range that has a single row.

Why select cells and rows instead of typing?


The main reasons are:
1. It avoids mistakes. What if we had typed 5:6 instead of 5:5? Selecting the row
ensures accuracy of reference cells.

2. Often we end up using arrow keys when typing. Mainly when we want to
insert brackets after typing the cell range. Or we may hit them by mistake.
Sadly, the arrow keys move the selected cell on the worksheet, and that
address comes up in the ‘Refers To’ box which is rather annoying.

3. Third and the most important reason, Excel will put in the correct sheet name
in the formula and also put in the ‘$’ signs. As we will see, we need these.

Another advantage is that we don’t have to worry about typing the ‘!’ mark at
the correct location or worry about putting single quotes around sheet names
in case the sheet name has a space in it.

Why retain the ‘$’ sign?


Do the following.
1. Create a named range, only this time, remove the ‘$’ sings in the ‘Refers To’
box.

2. After closing the Name manager box, click on any other cell on the same
sheet. Or better yet, go to some other sheet and click on a cell.

3. Now open the ‘Name manager’ box again, select the range just created, and
look in the ‘Refers To’ box.
One or more references would have changed. If we try and use this name in a
formula, we will most likely get a ‘#REF’ error.
Hence, always preserve the $ sign when creating named ranges.
Now that we have the basics cleared. Let’s proceed and create some other types
of ranges.

But, what if we want to create a range for Expenses?


If you thing that we can use the same offset formula, and change the value of the
row offset you are absolutely right.
The Expenses range begins 2 rows below and one column to the right of our
reference cell B5. The length is going to be the same. Hence in the Name
Manager, for the ‘Refers To’ box we have the following formula:
=OFFSET($B$5, 2, 1, 1, (CountA($5:$5)- 1))

10.2.2 Dealing with Layout changes


A member in the management team has requested a layout change.
1. They want report date indicated to the left of the table in the ref row.
2. We have to add 3 more rows, demand, production and inventory.
3. At the top of the table we need a summary of revenue and expenses since
week one. Readers working in the field of project management would be
familiar with a similar metric called “ITD (Inception Till Date) revenues”. In
order to keep the formatting cute, we will have to add new rows at the top of
our table.
Changes 2 and 3 are straight forward. We make three more named ranges for
demand, production and inventory and use the Revenues and Expenses ranges in
SUM function in the summary table. Our modified layout is shown in figure
10.2.2a
The insertion of a column at the left and a row at the top would change the
location of our reference cell, but Excel is kind enough to make the necessary
changes in the Name Manager.
We need to be concerned about the width of our range. The date column is now
included in our count and so our formulas will produce incorrect results.
We will see an alternate general purpose method for such problems in a later
section when we create a range with single column. For now, we will use a
method specifically suited to cases like the present one.
We begin by noting that all cells in the header column have the word “Week”.
Hence we can just use the CountIF function to count these cells and give us the
width of the range.
It will also relieve us of worries about any extra data being added any where in
the header row. Here is the formula.
=OFFSET($B$5, 2, 1, 1, COUNTIf($5:$5,”=Week*”))

Before we proceed to create the three new ranges, let’s take a minute and think.

Notice how we can align the design to our goals of maintainability and
flexibility

We can see that the formula for calculating the width of our range is used in 5
different names. Any change in the formula would require change at 5 places.
The same formula gets calculated and gives the exact same value in all 5 cases.
To avoid multiple copies of the same formula, we will save the result of
COUNTIF as another name. We could save it in one of the cells on our sheet but
there is a possibility that it might accidently get deleted or changed. Hence we
prefer a saved name.
We will do the same thing with our reference cell. That way, it becomes easy to
change it if required and makes the formulas more readable. We will also see a
more useful application of a separate name for the reference cell when we deal
with charts.
For the new table, the formulas and the modified Names are shown in figure
10.2.2b. Note that we have saved the result of the CountIf formula as a name
‘RangeWidth’.

Fig. 10.2.2a The modified layout with summary table and new rows


Fig. 10.2.2b Modified names in the Name Manager box.

10.2.3 Dealing with our expanding business


One calm month goes by. Our spreadsheet is serving its purpose quite well.
Then one day in a meeting, it’s discussed that our business is spreading to other
geographical territories. In order to monitor the health of individual territories
we would have to modify our spreadsheet.
Instead of the original single table we’ll now have three. One table for each of
the territories and, one consolidated table for the entire company.
Also, each of the territories should have their own summary table showing total
Revenues and Expenses. The new spreadsheet layout is shown in figure 10.2.3a
Fig. 10.2.3a Worksheet with new areas added.

Now we see a bit of trouble.


· Should we create named ranges for the new tables as well?
· How do we handle the calculations for Revenue and Expenses summary?
Why not consider the situation from the perspective of our 3 main goals?
· Flexibility: Considering that the business is growing, we may have to add
more tables in future for new areas.

· Maintainability: New features should use whatever existing features have
already been built and not break down too many of the old things.

· User-friendliness: Tomorrow if we hand over the workbook to a new and
junior team member, he or she should be able to add new tables by just
copying tables created earlier without worrying about changing a whole lot of
formulas.
Since the structure of our table (data and summary cells) remains same, we refer
back to figure 10.2.3a and observe that
1. For each table, the data in the revenue row starts at an offset of 2 columns to
the left and 4 rows below the cell holding the label “ITD revenues”.

2. The width of each row remains the same. Let’s face it, the number of weeks
that pass don’t depend on the number of areas that our company operates in.
So the value contained in the name “RangeWidth” does not change no matter
how many areas we add.
Let’s start by modifying the summary cells for the top level ‘Company’ table.
The formula for obtaining the range holding ITD revenues would be:
=OFFSET($F5, 4, -2, 1, RangeWidth)

Since this is a normal range of cells, we just pass it on to the sum function:
=Sum(OFFSET($F5, 4, -2, 1, RangeWidth))

Important Note: Notice that in the formula we have kept the Column absolute
and Row variable ($F5). Why? So that we can copy the formula to other rows
and the formula will vary accordingly.
If you have been doing the steps in a real worksheet, just click in cell H6 and
press (remember the shortcut from chapter 2?)
Ctrl + D

Go ahead and paste the formulas in cells H17 and H18. The numbers will get
updated.
Seems good. But wait! Entering the formula in cells H5 and H6? Wouldn’t that
make the dynamic named ranges we created earlier (‘Revenues’ and ‘Expenses’)
useless? Not quite. We will see another use for them a bit later.
Now notice this. Any time we want to add a new area, copy the rows 15 to 25,
scroll to the bottom, and paste. Update the Area name and the numbers for
Revenues, Expenses etc. That is it! We can even copy the bottom most table if
we like.
But why not copy the ‘Company’ table? We will get to that in a bit. For now,
let’s think about getting data into the Company table.
Remember the requirement for ‘Company’ table? It should show the combined
numbers for all the tables listed below it.
Consider the cell D9 in figure 10.2.3a. It should contain sum of values in Cells
D21, D33, D35 and so on. Also, when we enter the values for the coming week,
we should be able to copy that formula into the rightmost cell. What do we do?
Sure in D9 we can enter something like
= D21 + D33 + D35….

For Week2, we just click inside E9 and press Ctrl + R. But where’s the fun in that?
Not only is it boring, but for Expenses we sould have to click in cell D10 and
type
= D22 + D34 + D36….

What happens when we add another table, how many formulas do we update?
This is where conditional calculation formulas come to the rescue. Remember?
The ones we discussed in section 7.8 “Conditional Math”.
= SUMIF(CriteriaRange, CriteriaString, SumRange)

So, in cell D9 we sum all the cells in column D, below row number 15, where
the word Revenue appears in column C. In cell F12 we sum all the cells in
column F, below row number 15, where the word Production appears in column
C.
All we need to work out is the size of the ranges (the number of rows to be
included in criteria and sum ranges). Because remember, we should only include
values that are below the ‘Company’ table.
How do we do that? Here is the most straightforward approach.
Decide a large enough number of rows in advance. In our example, each area
requires a table with 10 rows. Even if we have 100 areas, we will need at most
1000 rows. We will be conservative and take 1500 rows. So our range starts at
row 14 and goes till row 1515.
The criteria range would be ’$C$15:$C$1515’. For column D the sum range
would be ’D$15:D$1515’.
The formula in cell D9 would be:
= SUMIF($C$15:$C$1515, “=”&$C9, D$15:D$1515)

Surely you must have figured out the reason for placing (and not placing) $ signs
at specific locations in the formula. Let’s just list it out to reinforce the principles
in memory. Please refer to figure 10.2.3a while reading.
1. We want to be able to copy the formula to the cells on the right. So the
column we are summing should be change (D to E to F and so on), but the
criteria column should remain fixed at C. Hence the $ sign for column C but
not for column D.

2. We want to be able to copy the formula to the cells in the rows 9 to 13, but the
rows to be summed (15 to 1515) should not change.

3. The criteria (Revenues, Expenses, Inventory, etc.) should be from the same
row where we are entering the formula. This number should change when we
copy the formula to the rows below row 9. Hence for cell D9, ‘9’ does not get
a $ sign.
The big question is: In how many cells do we need to enter this formula?
The big answer: Only one!
Here is what we do after entering the formula in cell D9.
1. Click in cell D8.

2. Press ’Ctrl + Right arrow’. This should take us to the right most column. In our
case its column K for Week8. But remember there could be many more
columns.

3. Press the down arrow key.

4. Press ’Ctrl + Shift + Left arrow’. The entire range till Cell D9 is now selected.

5. Release the ’Ctrl’ key.

6. Keeping the ’Shift’ key pressed, press the ’Down arrow’ key till all rows up to row
13 are selected. So now the entire range D9:K13 is selected.

7. Release the ’Shift’ key.

8. Hold down the ’Ctrl’ key and Press D and then R (or R and then D). And there
we have it, the formula is copied to all cells and each of them calculates the
correct values.
Now every time a new column gets added to the right, we simply copy the
formulas from the column on the left.
Now we address the question we raised a few pages back. When we want to add
a new area, why not copy the company table? The answer: Because it contains
all these formulas. Better to copy one of the Area tables since it only has static
data.
All that seems great. But what if we decide to add another row to all the tables?
Let’s say the count of Employees.
No worries. Insert a new row in each of the Area tables and fill in the numbers.
In the Company table, insert a new row. Just copy the formulas from the row
above. Done!

10.2.4 Dynamic range with a single column


Now let’s create a dynamic range that contains a single column. Consider the
table shown in figure 10.2.4.

Fig 10.2.4 Sample data for creating a dynamic range with one column

This is like a transactional table that captures data for sales made in different
regions. As time passes, more rows are added at the bottom of the table. We
notice the following:
1. Blank cells are present in some columns.

2. The ProductID column has data in all rows below the header row.
Duplicate values are allowed. There is no data above the header row.

3. The header row has no blank cell
We will use the ProductID column as our reference column to count the total
number of cells to include in our range. Cell B4 will be the reference cell.
The total number of cells to be included in the range is given by the formula (In
column B, count all rows and remove the header row):
=(CountA(B:B)-1))

Here is the formula for the range that we can assign to a name (does not include
the column header):
=OFFSET($B$4, 1, 0, (CountA(B:B)-1), 1)

10.2.5 Dynamic range for tabular data


Now that we know how to create a dynamic range that has a single row or
column. Can we combine these to form a table that expands or contracts as data
gets added or removed? Sure we can.
Again, what we require are a cell, a row and a column that can be used as
references.
For simplicity we will consider the table of figure 10.2.4. We assume that we can
add more columns to the right as required. Now, we proceed as follows:
1. Height of table = Total number of rows of data.
=(CountA($B:$B)-1))

2. Width of table = Total number of columns


=(CountA($4:$4))

3. Reference cell $B$4


The formula for the table range would be as follows (does not include header
row):
=Offset($B$4,1,0,(CountA($B:$B)-1)),(CountA($4:$4)))
10.3 Dynamic named ranges in (Array) formulas
Suppose we have to display a summary table on a separate sheet showing the
total revenue, expenses, and other figures at the company level. We can use
named ranges directly used in formulas in place of cell addresses.
In simple functions:
If we want to get the total revenues and expenses, we can use the following
formulas
=Sum(Revenues) and =Sum(Expenses)

This was already shown in section 10.2.2 and figure 10.2.2a.


In lookup functions:
If we have a self expanding table, we can use it in Lookup formulas. For
example, consider the dynamic table created in section 10.2.5. This table is
displayed in figure 10.2.4. Suppose in cell C2 we have the value ‘WF009JKN’
and in D2 we have the following formula:
=Vlookup($C$2, tblData,2,0)

tblData is the name given to the dynamic tabular range. In cell D2 we will get
the result ‘Wiring Fittings’
With Array formulas:
Now let us see one interesting case where we can combine named ranges with
array formulas. Please refer to figures 10.2.2a and 10.2.2b.
Suppose we have to report the average Gross Profit (GP) percentage since week
one. Consider a simple case of five weeks.
For week ( i ), gp is given by :
(revi - expi)/revi = 1-(expi/revi)

We sum such values for each week:


(1-(exp1/rev1)) + (1-(exp2/rev2)) ......+ 1-((exp5/rev5))

Which is same as:


Number of weeks - Sum of (exp/rev) for each week

On dividing this figure by 5 we get


1 - Avgerage of (exp/rev) for each week
Now revenues and expenses are in two ranges. However, the calculation requires
ratio of individual cells. That is, cell 1 of expense range divided by cell 1 of
revenue range and so on.
We know that array formulas can take entire ranges and perform operations on
individual cells. let's use this feature.
In a cell enter the formula and hit Ctrl + Shift + Enter this last bit is very
important.
= 1 - Average(Expense/Revenue)
10.4 Using dynamic ranges in chart series
Problem:
Our management, like all managements, loves charts. Every week new data
comes in and after updating our summaries we have to manually update the
bounds of our charts. Sometimes we forget to update the chart data even after
updating the base data and summaries.
(Not everyone forgets updating charts. We will see better applications of this
concept in coming sections, but for now let’s just focus on the concept)
Solution:
We can use the dynamic ranges, that we created earlier, for feeding data to
charts. That way, whenever the base data is updated, the charts get updated
automatically. Let us see how this can be done.
Look at the data in figure 10.2.3a. Suppose we want to create a chart showing
the weekly trend of Revenues and Expenses for the entire Company.
First step would be to create the main chart manually and put in all the
formatting, title and other fancy stuff. These things don’t change week on week
and we don’t need to update them automatically.
To create the chart, we would select the range “C8:K10”. Go to the ‘Insert’
ribbon. Then, in the ‘Charts’ section, select the type of chart. We will select a
2D-line chart just to keep things simple. But the concept can be applied to other
charts as well.

Now we can position and format the chart as required.
Once the main chart is created, we can proceed to use the named ranges in place
of static data.
To do this we need to create a dynamic range for the labels that will appear on
the horizontal axis. So far we have created dynamic ranges for Revenues and
Expenses. Now please go ahead and create one for the “Week” labels.
Steps for associating these named ranges to the chart are as follows:
1. Right click on the chart and in the popup menu click on ‘Select Data…’. This
brings up the ‘Select Data Source’ dialog box as shown in figure 10.4a
Fig 10.4a Select Data Source dialog box

2. In the ‘Legend Entries’ section, click on any series name to select it. Let’s say
we select ‘Revenues’.
3. Click ‘Edit’. This brings up the ‘Edit Series’ dialog box as shown in figure
10.4b

Fig 10.4b Edit series dialog box

4. The first box ‘Series Name’ has the address of the cell that contains the name
label for the series. Usually we can leave it as it is. However, we can enter a
different string if we want. For example, we can enter =”Revenues”.
5. The second box, ‘Series Values’ is important for our work. Here we should
enter the name of our dynamic range in the following format:
=<workbook name>!<name>
‘//** Notice the ‘ = ‘ and ‘ ! ’ **//

6. Click on ‘OK’. This takes us back to the ‘Select Data Source’ dialog.
7. Perform steps 2 to 6 for all the series on the chart.
8. In the ‘Select Data Source’ dialog, click ‘Edit’ in the section marked
‘Horizontal(Category) Axis Labels’. This brings up the ‘Axis Labels’ dialog
shown in figure 10.4c

Fig 10.4c Axis Labels dialog box

9. In the ‘Axis label range’, enter the name of the range created to hold the week
names.
10. Click Ok to return to ‘Select Data Source’ dialog. Click Ok again to exit.
That’s it!. Now each time we add (remove) a new week label and the
corresponding Revenues and Expences values, the chart will grow(shrink)
automatically.

10.4.1 Adding a new series:


At a later date if we want to add a new series to the same chart we proceed as
follows:
1. In the ‘Legend Entries’ section of the ‘Select Data Source’ dialog, click on the
‘Add’ button. This will bring up the ‘Edit Series’ dialog. However, the ‘Series
Name’ box will be empty and the ‘Series values’ box will have some one-
element array.

2. In the ‘Series Name’ box, input the name of the series as mentioned in setp 4
earlier. Or we can click inside the box and then click on the worksheet cell that
contains the label for the series.

3. In the ‘Series Values’ box, we will enter the name of the dynamic range as
described in step 5 earlier.

4. Click OK on the ‘Edit Series’ dialog, and then on the ‘Select Data Source’
dalog.
10.5 Choosing one out of many series
Let us see a more important use of named ranges. Consider the following:
Problem:
We receive a request that on the same chart we should be able to see the graphs
of revenues, expenses for the entire company as well as the individual areas.
However, the user should have an option to select between company and areas
using a dropdown as shown in figure 10.5a

Fig 10.5a Display different ranges on same chart based on value selected by the user

Solution:
Please refer back to figure 10.2.2b. We see that the named ranges Revenues,
Expenses and Rangewidth depend on one common parameter the reference cell
C8 on Sheet7. The address of this cell is currently saved as a name ‘RefCell’.
The reference cell is the top left cell of the first table shown in figure 10.2.3a.
This table shows the consolidated numbers for the entire company in the rows 8
to 13.
The figures for the individual areas are in other rows. For example, rows 20 to
25 show the revenues, expenses and other details for Area1. The top left cell of
the table for Area1 would be C20.
If we can apply a formula to the name RefCell to make it point to the correct
reference cell based on a value selected by the user, the dynamic ranges
‘Revenues’ and ‘Expenses’ will get updated accordingly and hence the charts
will also show tha appropriate data.

Note that we are using the features that we have already built. Refer back to
the discussion in section 1.4.

This is the initial plan. We will make a slight change when when we reach the
appropriate point. The change will allow us to avoid complicated nested
formulas and creation of Names that are not absolutely necessary.
Referring to figure 10.2.3a, we notice that the layout is such that:
1. For each table the reference cell will always be in column C. Only the row
differs.
2. The label marking each table (‘Company’, ‘Area1’, ‘Area2’) is in column B.
3. The number of rows between each label and the corresponding row of the
reference cell is fixed for each table. Four rows in our example.
With these observations in mind, we proceed as follows:
a) Selection of area
First, we allow the user to select the area for which the chart has to be displayed.
This selection can be made in cell C2 as shown in figure 10.5a
To create the dropdown, we will use cell validation with a hand typed list in cell
C2 as shown in figure 10.5b. Here are the steps:
1. Click in cell C2.
2. In the menubar click on ‘Data’ to reveal the ‘Data’ ribbon.
3. In the ‘Data’ ribbon, click on ‘Data Validation’. This brings up the ‘Data
Validation’ dialog box as shown in figure 10.5b.
4. Fill in the details as shown in the figure. Notice that the entries are exactly the
same as the labels for each table of data.
10.5b Hand typed validation list

b) Based on user’s selection, work out the reference cell


We can use the ‘Match’ function on column B to find the row number of the cell
where the table's lable is located. (Notice how the practice of keeping the layout
consistent between tables is helping. The same formula can be used to select
different tables.). Input for the ‘Match’ function comes from cell C2 of figure
10.5a.
To this number we add a fixed number (4 in our case) to get the top row of the
table, the one that contains week labels.
The tables are on ‘Sheet7’ and the chart is on a worksheet named ‘Graphs’
shown in figure 10.5a. Here is the first part of the formula
=MATCH(Graphs!$C$2,Sheet7!$B:$B,0) + 4

The output of ‘Match’ function combined with the fixed number can be used in
the ‘Address’ function to calculate the exact address of the top left cell of each
table of each area. Since reference cell is in column C, one argument in Address
function is always fixed. Here is the formula:
=ADDRESS(MATCH(Graphs!$C$2,Sheet7!$B:$B,0)+4,3,,,"Sheet7")

Note:
We already have one formula with complicated arithmetic operations nested
within another function. Instead of nesting this formula into another function,
we will simplify the process by saving the result of this formula into a cell (say
G10) on the ‘Graph’ worksheet.
This approaches provides the following advantages:
· In the new named formulas, we can use the reference G10 instead of the
whole formula.
· It will avoid creation of an extra named formula-we have avoided using the
‘RefCell’
· It will allow us to check if the formula calculates the correct address when
different values are selected in cell C2.

This approach should always be used when the formula returns a single value.
Named formulas should be used only when the formula returns a range of
cells. This approach will consume less memory and also improve calculation
speeds.

We can also this approach for storing value of RangeWidth in a worksheet cell
instead of a Name.

Note that the cell G10 is safely hidden behind the chart. This prevents
accidental changes or deletion.

The output of the ‘Address’ function is a string that can be used in the ‘Indirect’
function to get the actual range of cells to be displayed on the chart. Let us
modify the named formula ‘Revenues’
=OFFSET(INDIRECT(Graphs!$G$10),1,1,1,RangeWidth)

And here is the formula for ‘Expenses’:


=OFFSET(INDIRECT(Graphs!$G$10),2,1,1,RangeWidth)

Done! Now on the Graphs worksheet change the value in the dropdown in cell
C2 and see the changes reflected on the chart.
Now, suppose we have a new business area. We just copy one of the earlier
tables and paste at the appropriate location. Making sure we maintain the proper
number of rows between the bottom of an existing table to the label of the new
area and between this liable and the top of the new table
10.6 Charts with n-Period Rolling Ranges
Problem:
We want to create a calculation or chart that uses data only for a fixed number of
past periods.
Some applications would be:
Ø Calculation of rolling average of product demand.
Ø Showing trend of defective products for a rolling period of 12 weeks
In such cases, we often have to update the start and end of the concerned range
manually when we update the data.
Solution:
With our knowledge of named ranges, we can, at times, create a dynamic named
range that would update each time we add new data. Let us consider the example
we have been using so far.

Fig 10.6a Sample data

We reproduce the company level table from previous example. This time, with
11 months of data.
For this illustration, assume that we have to show four weeks of data on a rolling
basis. So this week we show data from week 8 to week number 11. Once data for
week12 comes in we show data from week 9 till week 12.
Notice the relation between the three numbers that we can try to use in our
solution.
Length of rolling period = 4
Ending week Starting week would
number be
12 12-4+1 = 9
11 11-4+1 = 8

Now, the ending week number is equal to the total number of weeks present in
the table at any time. This value is already calculated with the RangeWidth
name.
Note also that the number we calculated (starting week number) is the offset
from the first column of the table. The reference cell is the one with
"Particulars".
So here is the principle, for the series that we want to display on a graph on a
rolling basis, we first find the total number of values we have. From that we
work out the first value that will be displayed on the chart. Then display values
from the first calculated value till the last value.
So our formulas for week labels, revenues and expenses will change as follows.
Compare these with the formulas we created in sections 10.2.
In the formulas, the cell ’Sheet7!$R$4’ holds the length of the rolling period (4
in our case). So yes, we can allow the user to vary the length of the rolling
period if we want. The charts will get updated to reflect the change.
Name: WeekLabelsRoll
=OFFSET(INDIRECT(Sheet7!$R$3),0, _
RangeWidth - Sheet7!$R$4 + 1,1,Sheet7!$R$4)

Name: RevenueRoll
=OFFSET(INDIRECT(Sheet7!$R$3),1,_
RangeWidth-Sheet7!$R$4+1,1,Sheet7!$R$4)

Name: ExpenseRoll
=OFFSET(INDIRECT(Sheet7!$R$3),2,_
RangeWidth-Sheet7!$R$4+1,1,Sheet7!$R$4)

The reference ’INDIRECT(Sheet7!$R$3)’ is from the work we did in section
10.5
Fig 10.6b Adding the rolling range as a chart series

Fig 10.6b Chart created with rolling ranges.



The easy way out.
Create a chart displaying a fixed range of four cells. These cells should not be
part of the table holding the data. Hence they will be located in some other sheet.
Write a macro to copy the latest four values from the data table to the fixed
range. Of course we will have to give up the flexibility of being able to easily
add additional tables and of being able to vary the rolling period and areas for
which data is displayed. (Company, Area1, Area2…)
10.7 Country-State-City selection
(cell validation with dynamic ranges)
Let us now see a useful application of dynamic named ranges.
This section uses several concepts from Chapter 7 so you may want to refer back
to it as required.
Problem:
We have to create an input sheet where we have to provide 3 drop downs in cells
such that once a country is selected in one cell, only the states in that country are
available for selection in the next cell’s drop down and once a state is selected,
only the cities in that state are available on the next drop down.
Solution:
We will create the dropdowns using the data validation technique. The list in the
dropdowns will be provided using dynamic named ranges.


Fig 10.7

The worksheet where the dropdowns will be created is named as “InputForm”


and the layout is shown in figure 10.7.

10.7.1 General method for cell validation with a


named range
Suppose we have a named range (single column or single row) with the name
‘DropDownList’.
Click and select the cell where the drop down has to be provided.
In the ‘Data’ ribbon, click on "Data Validaton". The validation dialog box is
displayed as shown in figure 10.7.1

Fig 10.7.1 Data Validation box.

1. In the first dropdown, select "List".


2. In the box below we type '=' followed by the name of the range.
3. Click “Ok”

Now when we click in the cell again we will see a drop down list with the values
of the range listed there and available for selection.

10.7.2 Preparing the reference data


We have a sheet named "Location_List" (figure 10.7.2). On this sheet, in col A
we have a list of all relevant countries. noticwbthat there is no blank cell in this
entire column.
Column C onwards is where it really gets interesting. There is one tabular listing
for each country. The states are listed horizontally in the first row of the table. In
the column of each state, the cities within that state are listed vertically.
The leftmost column has the country name listed for each row. Its function wil
be clarified soon.
This is a compact arrangement that uses a bare minimum required
numbernofncells while still keeping the formulas quite simple.
This layout is shown in figure 10.7.2a. Notice that in Col C, the country name
should be listed for each row even if only one state has a city listed in that row.
Consider column E (British Columbia) for Canada.

Fig 10.7.2 Layout of data for Country-State-City selection.

10.7.3 Preparing the named ranges


Figure 10.7.3 shows the entries as they would appear in the “Name Manager”
dialog box. Notice that all the names are created in Workbook scope.
The range for country list is pretty straight forward and the formula is self
explanatory. We will save this as 'Country_List".
=OFFSET(Location_List!$A$1,1,0,COUNTA(Location_List!$A:$A),1)

Once a country is selected, we need to locate the table of that country listed in
columns C and beyond. We will do this by locating the row where the table for a
country begins.
We locate the row by using the match function on column C. We will use the
following formula that we will save with the name “Country_Row”. This is the
first purpose of having the country name in col C of the ’Location_List’
worksheet.
=MATCH(InputForm!$B$2,Location_List!$C:$C,0)

Notice that we are using the country name selected in cell B2 of “InputForm”
worksheet as input into the MATCH function. So the subsequent table selected
will depend on the country that the user selects.
Once the table is located, we need to find the number of rows and columns in the
table.
Although this can be accomplished by other methods that would require bit more
complicated formulas and few more names, we will adopt a simple approach.
We will use the COUNTIF formula to count the country name in column C. This
formula will be saved with the name “Country_Table_Height”. This is the
second use of listing of country names repeatedly in column C of
’Location_List’ worksheet.
=COUNTIF(Location_List!$C:$C,InputForm!$B$2) - 1

Remember, for this to work we need a country name in all the rows where a city
is listed. We reduce the number by 1 because we don't want the state name to be
listed in the city drop down.

Fig 10.7.3 Name manager box with names for ranges used for selection

The next number we need is the width of the country table. We do this by
applying the CountA formula to the country row. This formula will be saved
with the name “Country_Table_Width”.
=COUNTA(INDIRECT("Location_List!$" & _
Country_Row & ":$" & Country_Row)) - 1

If we dont have an entry in column ‘A’, the number would be one more than the
number of states listed for a given country. This would result in one blank entry
in the selection list. We can avoid this by using slightly more complicated
formulas or extra names, but for our purpose we will bear with one blank entry
at the end of the list.
The width of the table can now be used to create the list of states. Here is the
formula:
=OFFSET(INDIRECT("Location_List!C"&Country_Row), _
0,1,1,Country_Table_Width)

This formula is saved with the name “State_List”.


Once the user selects a particular state from the State dropdown, we need to
create the list of cities based on the first two selections. The following formula
creates the required list. Notice how it uses the already calculated value of state
selected by the user and the value of Country_Table_Height.
=OFFSET(INDIRECT("Location_List!$C$"&Country_Row),1, _
MATCH(InputForm!$B$3,State_List,0),Country_Table_Height,1)

This formula is saved with the name “City_List”. Now use the named ranges
“Country_List”, “State_List” and “City_List” to apply data validation to the
cells B2 to B4 on “InputForm” sheet and the selection list is ready.
10.8 Using Names in VBA
Using Named ranges in VBA is quite simple. All we need to do is get a reference
to the range that the Name refers to.
Every name defined in a workbook can be associated with a “Name” object in
VBA. A particular name defined in the workbook, can be accessed using the
“Names” property of the workbook in which the Name is defined. This property
provides access to the collection of all names defined in a workbook.
Once a particular name is retrieved, we use the “RefersToRange” property of the
name object to get a reference to the concerned range. The code below shows
how this can be achieved. Once we obtain a range object, we can operate on it as
required.
Dim rng As Range

Dim nm As Name

Set nm = ThisWorkbook.Names("Country_List")

Set rng = nm.RefersToRange



Value property
This is one important property of a Name object. It returns a string containing
the formula in the refers to box (even if the name refers to a constant). Addresses
of ranges are in A1 style notation.
The string starts with an equal to sign in the beginning so if we intend to use the
value in any further processing, we should remove the “ = “ sign first. The code
below prints the value property of a name in immediate window. Please refer to
figure 10.7.3 to gain clarity.
Debug.Print nm.Value

The output is the formula that we would see in the Name Manager dialog box for
“Country_List”
Chapter 11
Working with charts in VBA
In this chapter we will see how we can access and do some basic operations on
our charts in VBA.
Although VBA provides capabilities to work with charts at a very detailed level
(like setting values, color, labels and so on.) it is always advisable to proceed as
follows:
1. Create the data range (can be static or dynamic) for the chart.
2. Create a chart with all required formatting.
3. Use VBA to transfer a copy of the chart outside of Excel. Either as a separate
image or to other programs.
11.1 Naming a chart
Yes, we can give names to charts too. What good will that do? Well, not so much
as in formulas. But names for charts come in handy when we want to work with
charts from inside a macro.
So how do we name a chart? Pretty much in the same way that we name a cell
range. Refer to figure 11.1.

Fig 11.1 Naming a chart on a worksheet



1. First select the chart by clicking on it. We need to ensure that we select the
entire chart. Axis, title, background and all. Not just the part with the lines.
Looking at the figure, the selected area covers the cells from F1 to O18. This
is best achieved by clicking near one of the corners.
The selected area is called the “Chart Area” in Excel parlance. The part of the
chart area that holds the actual graph (lines, bars etc.) is called the “Plot
Area”.

2. Now as with the cell range, click inside the “Name Box”.

3. Type the desired name.

4. Press Enter (Do not forget this part).
If we do everything correctly, we should see the name in the Name Box if we
click on the chart area and select it.
That’s about it. We can now use this name in our macros to work with the chart.
But first we need to learn some basic concepts.
11.2 Object Model for working with Charts
ChartObjects collection:
Every worksheet in a workbook holds a ChartObjects collection. This is a set of
all charts that are created (technically: “Embedded”) in that worksheet. Each
item in the collection is called a ChartObject.
Let us assume we have created 3 charts on “Sheet2” of our workbook.
A particular ChartObject can be obtained in two ways:
1. Using the index of the chart like so
Dim co As ChartObject
Dim wks As Worksheet
Set wks = Thisworkbook.Worksheets(“Sheet2”)
Set co = wks.ChartObjects(2)

This method comes in handy when we want to do something with each
ChartObject present on the worksheet. The total number of charts is given by
the “Count” property of the collection. Hence we can have a ‘For’ loop as
follows:
For i = 1 to wks.ChartObjects.Count step 1
Set co = wks.ChartObjects(i)
‘//** Work with ChartObject (co) here **//
Next i

A better method to do this would be the ‘For Each’ loop. The code below
prints out the name of each ChartObject in the Immediate window.

For Each co in wks.ChartObjects
Debug.print co.Name
‘//** Work with ChartObject (co) here **//
Next co

2. Using the name of the ChartObject. Suppose that the second ChartObject is
named “RevExpChart”
Dim co As ChartObject
Dim wks As Worksheet
Set wks = Thisworkbook.Worksheets(“Sheet2”)
Set co = wks.ChartObjects(“RevExpChart”)

ChartObject object:
This object represents a particular ChartObject present on a worksheet and can
be obtained from the ChartObjects collection.
We won’t be using this object directly. But it is required in order to obtain the
actual chart which is item of interest. We can get this using the ‘Chart’ property.
Properties:
1. Name:
This is a string value holding the name of the ChartObject.
2. Chart:
This object represents the actual chart graphic that we can copy or save as an
image.
Dim co As ChartObject
Dim chrt as Chart
Dim wks As Worksheet
Set wks = Thisworkbook.Worksheets(“Sheet2”)
Set co = wks.ChartObjects(“RevExpChart”)
Set chrt = co.Chart

Methods:
Copy & Cut:
These methods transfer the ChartObject object to the Clipboard. The meaning of
these is same as the standard Windows Copy and Cut commands. These methods
are most useful when we want to transfer a copy of the chart to some other
application like Word or Powerpoint.
Once we Cut or Copy a chart from Excel, we have to use methods available in
the other application to paste the chart there. Specific techniques will be seen in
chapters 15 and 16 where we work with Word and Powerpoint.

Chart object:
Not to be confused with ChartObject object. This is the actual graphical
representation of our chart and is obtained using the Chart property of the
ChartObject Object.
The fact is, this is the actual object that we want to work with. Here are some
important properties and methods.
Methods:
1. Export:
This is method allows us to save the chart as an image file. We discuss the
method in detail in the next section.
11.3 Saving a chart as an image file
This is useful if we want to insert the graph as an image into some other report in
Word or Powerpoint format or we want to upload the image file to a web server
to be displayed on a webpage.
It is more useful if the image file has to be loaded to a webserver where it will be
displayed on a website. As noted in the previous section, the Export method of
the chart object provides a convenient way to do this. The syntax of the method
is as follows:
chrt.Export (filename, filtername, interactive)

Arguments:
a) filename: This is a string value and contains the name of the file to which the
chart will be saved. This argument cannot be omitted.
We need to provide the full path of the file along with extension.

b) filtername: This is an optional argument. It is of variant type and contains
what is called a Graphics Filter. For usual graphics formats like GIF and JPG
this can be omitted.

c) interactive: This is an optional argument. It is of Boolean type and dictates
whether Excel will display a dialog where the user can select settings for
Graphics filter. The default value is False, and we will go with that.
We want our macro to run in a way that minimum user intervention is
required. The code below will export our chart as a Gif file. The file is saved
in the same folder as the current workbook. Notice the use of ‘Path’ property
of Workbook object.
Dim co As ChartObject
Dim chrt as Chart
Dim wks As Worksheet
Set wks = Thisworkbook.Worksheets(“Sheet2”)
Set co = wks.ChartObjects(“RevExpChart”)
Set chrt = co.Chart
chrt.Export(ThisWorkbook.Path & "\chRevExp.gif")
Chapter 12
Other Cool Stuff
12.1 Trigger your macro when you are sleeping
Just trying to be playful with the title!
What it means is that we will look at ways to trigger our macros at a specific
time.
The technique depends on the scheduling capabilities of the operating system
and the code is written for Windows. Mac users would have to find a similar
option. Here is how we proceed.

Notes:
1. The technique described here requires that we switch off Excel’s macro
security. We should be very careful before running any macros that are
written by someone else.

If any of the concerned macros are written by other people, please run
those macros manually atleast once (on dummy data) before triggering
them automatically.

2. This technique requires that the launcher file is created in a new version of
Excel (2007 and above). If the launcher file has an extension of .xls, this
method can’t be used. The other files that contain the macro can be in
earlier versions, but the launcher file has to be in a newer version of Excel.

To enable all macros:
Click on “Developer” on the menu bar to activate the Developer ribbon. Then
click “Macro Security” in the “Code” group as shown in figure 12.1a

Fig 12.1a Macro Security setting

This would bring up the “Trust Centre” dialog box as shown in figure 12.1b
Select “Macro Settings” in the left hand pane.
In this dialog select the setting that says “Enable All Macros With
Notifications”.
Finally click on “OK”

Fig 12.1b Trust Center dialog box

Step 1: Create the launcher file


This will be an Excel file that has code in its “Open” event. That means,
whenever this file is opened a certain piece of code always gets executed without
any additional action. We can access the Open event of the launcher file as
follows:
1. Create a new blank Excel file.
2. Open the Visual Basic Editor.
3. In the project Explorer, go to the Microsoft Excel Objects list and double click
on the “ThisWorkbook” object as highlighted on the left hand side of the
figure.
4. In the code window, in the left hand dropdown, select “Workbook”. In the
right hand dropdown, select “Open”.
Excel automatically creates a blank Sub procedure for us.
Fig 12.1c Open event of ThisWorkbook object

In sub sections 12.1.1 amd 12.1.2 we will see how to write the code in “Open”
event depending on whether the macro(s) to be triggered is in the same or
different workbook(s).
Step 2: Schedule a Windows task
This is the step where we set the schedule for executing our launcher file. We
will be working with the Windows task scheduler.
a) Go to the Windows Control Panel and under “Administrtive Tools” look for
“Task Scheduler” or “Schedule Tasks”.
An easier way is to click on the “Start” button at the lower left of the screen,
and in the search box type “Schedule”.
This opens the Task Scheduler window as shown in figure 12.1d.

b) There are multiple ways to create tasks. We will go with the most basic steps
now. Other options can be explored once we are familiar with the basic steps.
Click on “Create Basic Task” option in the right hand pane marked “Actions”.
This is shown in figure 12.1d marked by number 1 in figure 12.1d. This step
opens the “Create Basic Task Wizard” dialog box as shown in figure 12.1e
Fig 12.1d Windows task scheduler


c) In the first screen of the wizard, provide a name and description for the task.
Click ‘Next’ to open the ‘Task Trigger’ screen of the Wizard shown in figure
12.1f
Fig 12.1e Create basic task dialog

d) Select one of the options in the “Task Trigger Screen”. Let’s say we want to
run our macro once a week, every week going forward. Select “Weekly” and
click “Next” to land on the next screen.

Fig 12.1f Task Trigger screen

Task Trigger screen allows us to set when a particular task will be executed

e) The next screen of the Wizard allows us to set the exact schedule. A sample is
shown in figure 12.1g. Enter the details and click “Next to open the “Action”
dialog box shown in figure 12.1h

Fig 12.1g Setting the exact schedule for the task

f) The Action denotes the “Type” of task we want to perform. Select “Start a
program” as shown in figure 12.1h and click “Next”. Clicking “Next” brings
up the “Start a program” dialog.

Fig 12.1h Setting the exact schedule for the task

g) The “Start a program” dialog is where it gets really interesting. Click on the
“Browse” button and select the launcher file that we had created earlier. Click
“Next” to reach the finishing screen. Click on “Finish”
Fig 12.1i Start a program dialog for selecting the file to launch

We are ready to go. Now, as per the schedule, the task will get executed, the
launcher file will open and based on the code in the “Open” event procedure, the
appropriate macros will get executed.

12.1.1 Trigger macro in the same workbook


Let us now look at the code that goes into the “Open” event if the macros that
we want to execute reside in the same workbook.
A point to note is that we don't always need a launcher workbook. If all the
macros that we want to execute are in the same file, we can open that file with a
scheduled task.
In such a case, technically, we can write our entire macro inside the open event,
but that is not the right thing to do. Instead, we break up our macro into a
number of subs and functions in different modules. Then we call those
procedures in the required sequence from within the Open event. Here is how the
code would look like.
Private Sub Workbook_Open()
Dim nResult As Integer
Call Module1.Sub1
Call Module2.CreateSummaryView
nResult = Module2.Function1
Call Module3.DivideNDisplay(nResult)

End Sub

The illustration shown here is quite simple, but we can have code that calls
procedures with more complex arguments and also performs operations on
worksheet ranges.
However, this approach keeps the event procedure clean and easy to read. If any
procedure requires a change, we can do it in the related module without going
through a large individual block of code.

12.1.2 Trigger macros in multiple workbooks


Problem
Suppose we have a list of different tasks to perform using several different
macros. these macros are in different files. if we use the solution in sectio 12.1.1,
we would have to create several different scheduled tasks which is not very
appealing.
Additionally, any new macro to be added or removed requires creating or
deleting scheduled tasks.
Solution
In our launcher file we can write code to open each of the different files and then
trigger the macros in those files.
How is this possible? Well, Excel’s Application object has a very useful method
called “Run”. This method takes the name of the another file and name of a
macro created in that file, along with any arguments, and executes the macro.
This method returns whatever the called macro returns. Here is the syntax for
this method.
Application.Run(strMacroName, arg1, arg2, ………,arg30)

Arguments:
1. strMacroName: This is a string containing the name of the macro (sub or
function procedure) to be executed. This is a required argument and has to be
specified in the following format

“’FileName’!ProcedureName”

Notice that the name of the file is enclosed in single quotes ( ‘ ). This is
followed by an exclamation mark ( ! ). Then follows the name of Sub or
Function procedure. This entire combination is enclosed in double quotes.
We can have this value in a string variable, and then just use the variable in
calling the Run method.

2. arg1 to arg30: These are upto 30 arguments that are passed directly to the
macro that is being triggered. These should be specified in the exact same
order as they appear in the declaration of the Sub or Function being called.
It is important to note that these arguments can only be values, we cannot pass
objects to the macros we call with this method
Here is a sample code in the Open event of a launcher workbook. Notice how
two workbooks are opened one after the other and multiple macros are
executed

Workbooks.Open("C:\Reports\ReportBook.xlsx")
Application.Run "’ReportBook.xlsx’!Macro1"
Application.Run "’ReportBook.xlsx’!Sub2"
Workbooks(“ReportBook.xlsx”).Close(FALSE)
Workbooks.Open("C:\Sales\Sales AutoRpt.xlsx")
With Application
.Run "’Sales AutoRpt.xlsx’!GetData", Date
.Run "’Sales AutoRpt.xlsx’!CreateReport"
.Run "’Sales AutoRpt.xlsx’!SaveToPPT"
End With
Workbooks(“Sales AutoRpt.xlsx”).Close(FALSE)

12.1.3 Managing existing scheduled tasks


When a scheduled task is created it appears in the main window of “Task
Scheduler” as shown marked with 2 in figure 12.1d
If we right click on the task we get a pop-up menu that allows us to manage the
task in various ways. Let’s look at the various options in the pop-up menu
Option Function
Run Runs the task immediately
Delete Deletes the task from the scheduler
Disable Disables a task temporarily. The task will not execute at the next
scheduled run.
To enable the task again, just right click on the task again and select
‘Enable’
Properties This brings up the task properties dialog box that has various tabs
as shown in figure12.1.3
The General, Action and Trigger tabs allow us to modify the
various settings.
Selecting the item and clicking on “Edit” button opens up various
boxes that we saw while setting up the task.
The other tabs have additional options that we can set but it is
recommended to keep the default options. The options are self
explanatory and the reader can explore them if required.

Fig 12.1.3 Task properties dialog box

12.1.4 Editing the launcher workbook


Problem:
We want to open and edit the Launcher workbook itself. However, when we
open it, the Open event is triggered and the macros get executed when we don’t
want them to.
Solution:
Don’t open the launcher workbook in the usual way. Do as follows:
1. Launch Excel from the start menu
2. Click on the “File” tab in the menu
3. Click on Open. We have two scenarios now
a) File is available in the recently opened file list.
In this case, hold down the Shift key and then click on the file name. Keep
holding the Shift key till the file opens and is available for editing.
b) We browse to the file
Browse and locate the launcher file. Do not double click on the file
Click and select the file in the file-open dialog
Hold down the shift key and click on the “Open” button. Keep holding the
Shift key till the file opens and is available for editing
12.2 Zip and Unzip files with VBA
In this section we will see how we can create as well as extract zip files using
VBA.

12.2.1 Creating Zip files


The code we are going to write will automate the following actions that are
usually done manually:
1. In the explorer view, right click and select ‘New’ – ‘Compressed Zip Folder’.
This would create an empty zip file.
2. Now click and select one or more files that we want to include in the zip
folder.
3. Right click and select ‘Copy’.
4. Now click on the empty zip file. Right-click and select ‘Paste’.
5. Keep adding more files as required.
Here is how the code would work:
Ø The empty zip file can be created using the File I/O functions of Visual Basic.
However, to mark that file as a zip folder and to add files to it, we will use the
Shell interface of Windows in our macro.

Ø The empty file created using Visual Basic functions does not become a real
Zip file until we add a 22-byte long string of data into it. These 22 bytes are
what mark a file as a zip file. The string is called “End of Central Directory”
or EOCD record. It is the minimum requirement for a zip file even if the file
has no other content.

Ø Once the empty zip file is created, we can mark it as a ‘Shell folder’ and add
files into it using the methods available in Shell interface.
For purpose of our macro, we will put all the files to be zipped inside a separate
folder. The files need not be just Excel files. We can have PDF files, Word
documents or image files.
The path of the folder to be zipped is passed to our subroutine as a string in the
argument ’strSourcePath’. The path of the zip file to be created is passed as a
string in the argument ’strZipPath’.
We need to ensure that the zip file that we want to create doesn’t already exist. If
it does, we should delete it using methods of FileSystemObject, as shown in the
following code snippet, before calling the sub-procedure to create the zip file.
The FileSystemObject and its methods are described in detail in chapter 14.
If fso.FileExists(strZipPath) Then
fso.GetFile(strZipPath).Delete
End If

In this section we will use some special functions that are built into Visual Basic.
These functions are not required when we are writing macros in general, so we
will not study them in detail but, we will briefly see how they work when
combined together. Our purpose will be limited to creating and extracting zip
files.
Usually these functions should not be used in Excel macros. However, this
solution is included in this book because extracting input files from zipped
folders and zipping output files for distribution is a very common task.
Let's start with the ‘Open’ function.
This function allows us to open and work on files (binary as well as text files)
residing on our hard disk. However, use of this function is best left to advancd
programmers who are creating applications purely in visual basic and not writing
a macro.

We already have a better and straightforward method of reading and writing text
files using TextStream object described in chapter 14. As for binary files, we
should never open them directly from within an Excel macro. Instead, we should
automate the applications that are meant to handle them. Such techniques are
described in chapters on Outlook, Word and PowerPoint.

For our purpose, we will use the ‘Open’ function only in this special instant
when we want to automate the task of creating zip files. The syntax for this
function is

Open <filepath> for output as #<filenumber>

The argument ’filepath’ is a string holding the path of the file that we want to
open.
The terms 'for output' tells the program(macro) that we want to write something
to the file, our program will output something to the file.
The ’filenumber’ argument is an integer with which our program can refer to the
file as if it were a variable. The difference is that unlike variables that should
have alphanumeric names, Visual Basic refers to files using integers. These
numbers will be in the range of 1 to 511 roughly based on the order in which the
files were opened or created. We don't need to worry about the exact mechanism
of how the numbers are assigned.
Now here is the part that requires attention. if the file we specify with the
’filepath’ argument does not exist, the ’Open’ function will automatically create
it. The question is, since some file numbers may already be in use, what integer
should we use to represent the new file? We will leave it up to the program and
the operating system to decide that.
This is where the ’FreeFile’ function comes in. This function will simply retrieve
the next available file number and provides that to our code. We will store the
value in an integer variable and use it in the Open function to create the file.
Once our file is created, we need to put the EOCD record in it. For this we use
the ’Print’ function. The syntax for the function is
Print #<filenumber>, content

The ’content’, in our case, is a specific sequence of bytes representing the EOCD
record.
Now, the EOCD record is a sequence of bytes. The problem is that majority of
these bytes are not printable characters, meaning we can’t simply type them from
our keyboards. So we will use the ’Chr’ and ’String’ Visual Basic functions.
The ’Chr’ function takes an integer that is the ASCII code for the character we
want to produce and returns the character. Its syntax is as follows
Chr(<ASCII code>)
We will use it to produce the first 4 characters of the EOCD record. First two are
the alphabets ‘P’ and ‘K’. The next two are non-printable characters.
The ’String’ function (note this is not the String datatype) takes the ASCII code
for a character and another integer. It then returns a string where the specified
character is repeated as many times as the second integer. The syntax is:
String(<Repetition> , <ASCII code>)
Now the last 18 characters of the EOCD record are all ‘NULL’ (used to represent
absence of a value in programming). It is different from Space (“ “) or Zero ( 0 ).
ASCII code of the NULL character is zero. Accordingly, for our purpose the
syntax required will be as follows:
String(18, 0)
The values returned by ’Chr’ and ’String ’ functions can be combined using the
usual string concatenation operator ( & )
Once the EOCD record is placed we use the ’Close’ function to close the file.
Now the file is marked as a compressed zip folder and the Windows operating
system will treat it like that.
Once we have an empty zip folder we need to add contents to it. This is where
we need to invoke the Windows Shell.
For our purpose, the Windows Shell can be thought of as a virtual representation
of thee hard disk in the windows operating system. Beware, this is not an
accurate picture, but we don't need the technical details.
To invoke the Shell & get a reference to it, we will use Visual Basic's
’CreateObject’ function. This function takes a string (the name of a certain
object that we want to create) and passes the request to the operating system
(OS). The OS will invoke the correct application, create the object and provide a
reference to it.
Each folder on the disk is called a ’Namespace’ in Shell. The files inside the
folders are called ’items’ and are held in the items collection.
A Namespace is obtained using the ’Namespace’ property of the ‘Shell’
application. The parameter to be passed is the path of the required folder
(existing or new one to be created). The catch is that ’Namespace’ property
requires the parameter of Variant datatype. If we simply type the path in the code
things will work. However, if we pass a string variable an error gets generated.
To overcome this problem we use the ’CVar’ function of Visual Basic to convert
a string variable to a Variant holding the same value. So the syntax to get a
reference to the required Namespace is
<Shell application>.Namespace(CVar(<strPath>))
Once we have a Namespace object we will use its method called ’CopyHere’
which takes an ’items’ collection as an argument and creates a copy of those
items inside itself.
We will use two Namespaces. The first, for the zip file we want to create. The
second for holding the files that we want to add to the zip file. The ’items’
collection of the second namespace will be passed as argument to the
’CopyHere’ method of the first.
Sub to create a zip file from files stored inside another folder
Sub CreateZipFile(ByVal strSourcePath As String,_
ByVal strZipPath As String)

Dim oApp , zipFolder, sourceFolder As Variant
Dim intFile As Integer

‘//** Get the next available new file number **//
intFile = FreeFile
‘//**Open the zip file for writing Binary data **//
‘//** since the file does not exist, it is created **//
Open strZipPath For Output As #intFile
‘//** Put in the EOCD record **//
Print #intFile, _
Chr(80) & Chr(75) & Chr(5) & Chr(6) & String(18, 0)
‘//** Close the file **//
Close #intFile

‘//** Invoke the Windows Shell **//
Set oApp = CreateObject("Shell.Application")
If oApp Is Nothing Then
MsgBox "Could not create zip application"
Exit Sub
End If

‘//** Mark the newly created Zip file as a Shell Folder **//
Set zipFolder = oApp.Namespace(CVar(strZipPath))
‘//** Mark the folder to be zipped as a Shell Folder **//
Set sourceFolder = oApp.Namespace(CVar(strSourcePath))
‘//** Copy the files inside Source Folder into the Zip Folder **//
zipFolder.CopyHere (sourceFolder.Items)
End Sub


Notice how the various Visual Basic functions (CreateObject, CVar, Chr, String,
Open, Print, Freefile) have been used in the code. Now we will use them to
unzip the files.

12.2.2 Unzipping files


Imagine that we have a zip file and we want to unpack its contents into another
folder.
The code below takes the path of the zip file and the destination folder and
unzips the zip file into the destination.
Before calling this sub-routine, we need to ensure that the zip file and the
destination folders exist. If the folder does not exist, we need to create it using
methods of the FileSystemObject object before we call our sub-procedure. The
following lines of code can be used.
If Not (fso.FolderExists(strDestFolder)) Then
fso.CreateFolder (strDestFolder)
End If

The following code shows a Sub-procedure to unzip a zip file and store contents
inside another folder
Sub UnZipFiles(ByVal strDestFolder As String, _
ByVal strZipPath as String)
Dim oApp As Object
Set oApp = CreateObject("Shell.Application")
‘//** Note that the following is a single line of code **//
‘//** Notice the ‘_’ at the end of the first line **//
oApp.Namespace(CVar(strDestFolder)).CopyHere _
oApp.Namespace(CVar(strZipPath)).Items
End Sub
12.3 Up(Down)loading files from web servers with ftp
Problem:
There are times when we have to put files on a server. The files may not
necessarily be in Excel format. They could be Comma Separated Values (csv) or
Images (for graphs).
Here are some reasons for doing so:
1. The server has a common repository from where our team members can
download the required files.
2. The server hosts a website. The server administrator could write a program to
read our files and then plug them into the website.
At times we may also have to fetch our input files from such servers.
Solution:
In such situations, if we have an FTP account on the server, we can build macros
that would automatically load the files in the correct location. We can even
download the required files from the servers using ftp.
‘FTP’ is short for File Transfer Protocol, which is a standard way to transfer files
between computers over the internet.
VBA does not have a built in function for FTP so we have to use some
workaround. Windows has a built in FTP utility but it does not employ the
secure FTP (SFTP) protocol that servers require nowadays. So, we must use a
third party software that can be automated with VBA.
We will use the WinSCP utility. WinSCP is a freeware that can be installed on
Windows and we prefer it because it well accepted in organizations as being
reliable and secure. WinSCP cannot be linked to VBA directly, but it can be
operated via the Windows command line. VBA has a way to send commands to
the Windows command prompt (without displaying it).
One more advantage of WinSCP is that it supports scritpting. Meaning that we
can store the various instructions for up/download in a text file and then just
command WinSCP to carry out those instructions in a sequence. We can create
text files from VBA. We will see the detailed procedure in chapter 14. Here we
will see the bare minimum functions required. Interested readers can digress for
a while and refer to section 14.3.
If we put all these facts together, we have a way to automatically upload and
download files. Let’s look at the technique in detail.
We will consider a basic setup where we have an account on an FTP server
accessible with a username and password. This is the setup we would normally
encounter more so if we are operating a personal website with some hosting
provider.
There are slightly different scenarios of connecting to the server that require the
use of things called “hostkeys” and SSL certificates (please don't worry about
the acronyms). In such cases, please consult the server tech support team and the
WinSCP documentation.

In any case, only the part for connecting to the server will change. Rest of the
procedure remains the same.

Note: This solution requires the use of FileSystemObject and its related
objects and procedures. This object is described in detail in chapter 14. The
reader may have to refer to that chapter at certain times. Appropriate cross
references are provided in this section whenever matter from chapter 14 is
used in the solution.

Get WinSCP
Do a Google search for "WinSCP" or, alternatively, go to visit -
https://winscp.net
Get the binary file for WinScp and install it. The website also provides extensive
documentation on scripting and alternative ways of using winscp.
Learn the basic WinSCP commands
Winscp has a long list of commands for various purposes. We will look at the
most basic ones required for up/downloading files.
Open
This command is used to establish a connection with the server. The syntax is

Open <protocol>://<username>:<password>@<domain.com>/
The parameters in the angle brackets have to be entered for a particular user.
“domain.com” is the site or server. Do not forget any of the “/”, “:”, or “@”
characters. Instead of domain name we can enter the IP addrest of the server.
Protocol is either, ftp, sftp of ftps and we are notified about it when the account
is first created. Here is one example with the IP address instead of the domain
name
Open sftp://myUserName:[email protected]/

Note: The IP address stated in the code above is fictitious. Any
resemblance with an actual IP address is purely co-incidental and not
intended by the author.

cd
For winscp, the folders where files are stored are called directories. It is the
common term used for folders on major operating systems like Unix and Linux.
All the up/down loading activities happen in a particular directory on the server.
That directory is called the “current working directory” or CWD in short.
When we log in, we may land up in the root directory of the server. However, we
may want to up/down load files in some other directory. In that case, the cd or
“change directory” command allows us to switch to the required directory. The
syntax for this command is as follows:

CD <full path of folder on the server>

The folder path should start from the root level. For example, for a typical
website, the cd command can be....

cd /home/site1/public_html/wp-content/plugins/newPlugin/

Put
This is the actual command that uploads a file on to the server. The syntax is

Put "<file path on local computer>"

Notice that the path should be enclosed in double quotes. This is the path on the
local computer (the one where our macro is being executed). The following set
of commands will load three different files from three different folders on the
local computer to the current directory on the server.
put "c:\New Reports\rpt_setup.pdf"
put "c:\Sales\Sales chart.JPG"
put "c:\Issues\Issue file.txt"
Get
This command downloads a file from the current working directory on the server
to a specified folder on the local computer. The syntax is
Get “<source file name>” "<destination path-local computer>”

Notice that the paths have to be enclosed in double quotes and sepatated by
spaces.
The commands below fetch files from the current working directory to two
different folders on the local machine.
get "service list.pdf" "c:\Input files\"
get “Dashboard.xls” "c:\New Reports\"
get “testfile.txt” "c:\Jan Sales Input\"

To fetch files from some other directory, use the cd command to first switch to
that directory.

Close
This command will terminate the connection from the server. There is no special
syntax. It is just a one-word command.
Exit
This commands stops the winscp program and returns control back to windows.
There is no special syntax. It is just a one-word command.
rm
This command deletes a file in the CWD. The syntax is:

rm “<file name>”

Notice the double quotes around the file name. Command below deletes an
image file
rm "revchart.PNG"

Command below will delete All text files in the current directory
rm *.text
rmdir
This command deletes an entire directory that is located in the CWD. The
directory that is being deleted should be empty. This command will cause an
error if there is even a single file or directory inside the directory being deleted.
Hence to delete a directory, first delete all the files inside it using rm then delete
the directory with this command. The syntax is:
rmdir “<directory name>”
Mkdir
This command creates a new directory in the CWD. We need to ensure that a
directory with the same name doesn’t already exist, else the command causes an
error. The syntax is:
mkdir “<directory name>”
Option
This command helps us to modify some of the behaviours of WinSCP when
operated via a script. There are a lot of options that can be turned on or off, but
we would work with only two and even these are not absolutely essential.
Following command stops the entire up/downloading operation if any single line
in the script causes an error.
option batch abort
Following command turns off the feature that asks for confirmation when trying
to upload files into a directory when files with same name already exist. The old
files are overwritten
option confirm off
#Comments.
In a winscp script file comments start with a #. A line containing a comment
should have # as the first character. This line is ignored and is not executed.
In case we are buiding a file that will be executed multiple times, it helps to add
comments so we can change it easily in future.
Create the script file from inside VBA
The next step is to create a text file with all the WinSCP commands in it. The
code shown here is for creating a script file for uploading files to the server. The
script first creates a new directory on the server and then uploads 3 files to that
directory. The script file created is shown in figure 12.3a
The script for downloading will be same except instead of “Put” command we
will have to write the “Get” commands. Comments have been added, but these
are not necessary.
Note: The code shown here requires use of the FileSystemObject. This object is
not present in VBA by default and has to be included as a reference. Please see
section 14.1 for details on how to do this.
Notice how the double quotes are added to the file paths using “”””
The variables ’strFile1’, ’strFile2’, ’strFile3’ are strings that hold paths of files to
be uploaded. In the example the paths are directly assigned to the variables, but
these can be taken from properties of File objects as described in Chapter 14.
Sub CreateScript()
Dim fs As New scripting.FileSystemObject
Dim ts As scripting.TextStream
Dim strFile1, strFile2, strFile3 As String

Set ts = fs.CreateTextFile(“C:\Scripts\winscpput.txt")

strFile1 = "c:\New Reports\rpt_setup.pdf"
strFile2 = "c:\Sales\Sales chart.JPG"
strFile3 = "c:\Issues\Issue file.txt"
ts.WriteLine "option batch abort" & vbCrLf & _
"# Disable overwrite confirmations" & vbCrLf & _
"option confirm off" & vbCrLf & _
"# Connect to SFTP server with password" & vbCrLf & _
"open sftp://user:[email protected]/"
ts.WriteLine "cd " & """" & "/home/site1/public_html/" & """"
ts.WriteLine "MkDir " & """" & "todays_files" & """"
ts.WriteLine "cd " & """" &_
"/home/site1/public_html/todays_files" & """"

ts.WriteLine "put " & """" & strFile1 & """"
ts.WriteLine "put " & """" &strFile2 & """"
ts.WriteLine "put " & """" &strFile3 & """"
ts.WriteLine "Close"
ts.WriteLine "Exit"
ts.Close
End Sub

The script file created is shown in figure 12.3a

Fig 12.3a Text file containing script for uploading files to servers using WinSCP

Execute the script file from VBA.


Before we execute our script we need to keep at hand two pieces of information:
1. Full path of the folder where WinSCP is installed. Reader may have a
different path based on the installation. For our example, assume it is
“C:\Program Files\WinSCP\winscp”

2. Full path of the folder where our script file is stored. For our example, assume
it is
"C:\script files\winscpput.txt"

We can execute our script manually from the windows command line. To do that
we need to bring up the windows command prompt either by typing “cmd” in
the app search box or, in older versions, by going to “Start button-> Run” and
typing “cmd”
At the command prompt we can type as follows (all in one line):
"C:\Program Files\WinSCP\winscp" /script="C:\scripts\winscpput.txt"

Notice the double quotes around the paths.
Now, we want to execute the same command from inside our macro. This is
where we use the Visual Basic ‘Shell’ function. Here is how this function works:
Shell function
This function runs an executable program and returns a positive if successful,
otherwise it returns zero. The syntax is as follows:
Shell(pathname[,windowstyle])

Arguments:
1. pathname: This is a string containing the full path of the file of the executable
(mostly with an .exe extension, and any ‘command line arguments’). The
documentation of the concerned application will tell us about the command
line arguments. In case of WinSCP, the path of the script file is the argument.
This is a required argument.

2. windowstyle: This is an optional argument. It is an integer and specifies the
size of the window that will open when the program is launched. The
allowable values are:
Value Description
0 Window is hidden
1 Window is restored to its original size and position.
2 Window is displayed as an icon (minimized). This is the
default value
3 Window is maximized.
In all these cases, the launched program receives “Focus” meaning any mouse
or keyboard input is directed to the program. If we want to work with Excel or
any other program, we need to click on the relevant window.
As an example, consider we want to start “Notepad” we can use the following
line of code
Shell("C:\WINDOWS\system32\notepad.exe")

Getting back to our use of WinSCP, the following Sub procedure first calls the
CreateScript sub and then runs the script
Sub CreateAndRunScript()
Dim strCmd As String
Dim retVal As Double
Call CreateScript
strCmd = """" & "C:\Program Files\WinSCP\winscp" & """"
strCmd = strCmd & " /script="
strCmd = strCmd & """" & "C:\scripts\winscpput.txt" & """"
retVal = Shell(strCmd)
End Sub

Notice how the command string has been compiled. The paths have been
surrounded with double quotes.
Why not execute a series of commands from VBA? because once WinSCP is
activated control passes to that application and subsequent windows DoS
commands may not be recognized. Our method is more reliable.
Chapter 13
Working with Email attachments
In this chapter we address the task of getting our input files from email
attachments and sending our finished work to others via email.
The email client software that we discuss in this chapter are by themselves
specialized tools and one chapter in one book will not be enough to discuss all
their capabilities. Instead, we focus our attention on solving certain specific
problems. Here is one of them:
Problem-1:
We receive periodic reports or data from a wide variety of team members. These
might be our sales team, members in other departments, customers and so on.
This data is sent to us over email.
We need to look for mails received on or after a certain date, from a certain
person, having a certain subject. Once we locate the email, we need to download
the attached data files. Then we can compile, analyze and refine the data to
extract information.
Along with the analysis that we want, the tasks of searching emails,
downloading files and compiling data can be automated in Excel.
Here is our solution scheme for searching the required emails and downloading
the attachments therein.
Solution scheme:
Log in to the email client (Outlook or Lotus Notes) with our credentials and go
to the Inbox
Get a smaller representation of the Inbox in which we can search quickly. The
actual Inboxes holding the emails are quite slow when we have to search for
emails one by one. Let us call the representation of emails as ‘entries’
If possible, filter the representation based on date or sender’s email address.
Now search through the smaller set of entries for the required subject or sender
name.
Once we get a required entry, get a reference to the underlying email.
Remember, we are working with a representation of the emails, not the emails
themselves.
Once we have the actual email, open it and download the attachments.
In this chapter we will look at ways to automate these steps in two of the popular
email clients MS Outlook and Lotus Notes.
Problem-2:
Another problem that we will deal with is to automatically send mails using an
Excel macro.

Solution:
This one will be easy because Outlook and Lotus Notes both have built in
functions that can help us to compile and send emails.

Note: Excel has a SendMail method that allows us to send the current
workbook (one containing our macro) via the default email system to a
number of receipients.
However, our focus is to find a general method not just to send the current
workbook but any other file(s) which may not be Excel workbooks. Also the
methods that we will explore, will allow us to send separate mails and
attachments to different people.
13.1 Working with Microsoft Outlook
Microsoft Outlook is an email client that comes bundled with Microsoft Office.
Like other Microsoft programs, this one too is extremely user-friendly, both for
onscreen users, and for people trying to automate it.
Note that Outlook is an email client. Meaning that just having Outlook does not
allow us to send/receive emails. We need to couple it with an email server or an
ISP’s email account to start using it.
For readers working in organizations where Outlook is the official email client,
your IT department will configure your account on Outlook with an Exchange
Server (a type of email server).
When we use Outlook on our own, we can configure it with our email accounts
with Gmail or Yahoo. We will see how to do this in the coming sections.
Outlook allows us to create multiple accounts with either a single consolidated
Inbox for all accounts or separate inboxes for each account.
We start by looking at the most important objects of Outlook’s object model.
Then we move on to using these objects for our basic tasks.

13.1.1 Coupling Outlook to Excel


To use Outlook through Excel, we need to set a reference to Outlook’s object
library in our Excel workbook’s VBA project. To do this, in the Visual Basic
Editor window, click ‘Tools’ on the menu bar and then click ‘Reference’.
In the ‘References’ dialog box that comes up, scroll down till you see the
‘Microsoft Office x.y Object Library’. x and y are numbers specific to Outlook
library version that may be available on our computer.

13.1.2 The Outlook Object Model


The diagram shown here demonstrates the elements of Outlook’s object model
that are most important for developing our solutions.
The connections between objects should be viewed more from a point of access
rather than containment. For example, in Excel, a workbook contains a
worksheet. But in Outlook, a DefaultFolder does not contain a Table, but we can
access a Table representation of the DefaultFolder through methods of the
DefaultFolder object. The code examples will clarify the techniques.

Let’s take a look at the objects:


Application:
As with Excel, the Application object is the top-most object in the Outlook
object hierarchy. It is the parent for all other objects in Outlook.
We require only one Application object for our entire macro. Hence, we can
instantiate the Application object at the time of declaration.
Dim olApp As New Outlook.Application
Methods
1. GetNamespace:
This method returns a Namespace object. This object is the gateway to login
and access data for a profile. We describe it in detail a bit later. This method is
significant because without a NameSpace we cannot access any data in
Outlook.
There is only one Namespace in Outlook which is the default one. Its name
has to be supplied to this method. Hence the syntax for this method is:
Dim olNs As Outlook.Namespace

Set olNs = olApp.GetNamespace("MAPI")

2. Quit: This method closes the Outlook session. The user is logged out of the
messaging system and any unsaved changes are discarded.

3. CreateItem: This method is used to create a new item. This will be useful
when we create a new email that we want to send. In that case the syntax
would be:

Dim olMail as Outlook.MailItem

Set olMail = olApp.CreateItem(olMailItem)

Each unique item in Outlook is called, well, an ‘Item’. The type of item is
based on its purpose and decides the location where it is stored.
For example:
· Emails are objects of type ‘MailItem’. These are stored in the Inbox
folder
· Contacts are objects of type ‘ContactItem’ and are stored in the
Contacts folder.
Most of these items would be created using the front end (GUI). We will
create only the MailItems from inside our macros.

NameSpace:
As noted already, this object is a gateway to the data of a particular profile.
If we have multiple profiles configured on a computer, we need to use the
‘Logon’ method to access a particular profile and start a session.
We will assume that we are working with a single profile on one computer.
Hence when Outlook is launched, we are directly logged in to the user’s profile.
Properties:
1. Accounts:
This object is a collection of all accounts set up for a particular profile. A
particular Account can be accessed using the ‘Item’ property of the collection,
and specifying the DisplayName of the Account. More on this when we
describe the Account object.
Note: if we have only one account setup on our computer, we can access the
data in that account directly without having to deal with the Accounts
collection.
Methods:
1. GetItemFromID:
This method is used to retrieve items stored in a particular folder under a
given namespace. Each item is given a unique ID called the ‘EntryID’. We
can use the entry ID to retrieve the object.
For now, we will just see the syntax of the method. We will see a concrete
example later in the chapter when we use this method to get an email from
our inbox.
Syntax for the method is as follows:
olNs.GetItemFromID(entryID, entryStoreID)
Arguments:
A) entryID: This is a long integer value holding the unique ID of an object
stored in Outlook.
We don’t have to be concerned with the exact value of the ID. We can
retrieve it in a variable using indirect methods.
However, this is a required argument and cannot be omitted. We will see
examples where we use this method and the EntryID to retrieve specific
emails.

B) entryStoreID: This is a string that identifies the delivery store (described
in a later section) from which we wish to retrieve an Outlook item.
This is an optional argument and we will omit this argument when we use
this method because we will access the default delivery store.
Account:
Account object represents one single email account configured in Outlook.
We can setup multiple accounts for any given profile. For example, one Gmail
account, one Yahoo and one account of our corporate mail server. (Assuming
off-course that our company allows us to use our personal mail)
More specifically, an account is a collection of folders for a particular profile.
Outlook organizes our data in folders. There are folders for received
emails(Inbox), a folder for contacts, a folder for Sent emails and so on.
Some of the folders are default, built into each account by Outlook when the
account is setup and some are created by the user.
A particular account can be accessed using the “DisplayName” property and
fetching it from the Accounts collection. The code for doing this would be as
follows:
Dim olAcc As Outlook.Account
Set olAcc= olNs.Accounts(strDisplayName)

Here ’olNs’ is the NameSpace object we saw in the previous section.


’strDisplayName’ is the DisplayName of the Account that we describe now.
Properties:
1. DisplayName:
This is a string value that holds the name of the account as it is displayed in
the Outlook application.
The easiest place to find this property is in Outlook’s default screen or Home
tab, as shown in figure 13.1.2a

Fig 13.1.2a Outlook home screen showing the accounts and the different folders


Another place to look for it is in the “Account Settings” dialog box as shown in
figure 13.1.2b. The DisplayNames of the two accounts are greyed out in both the
figures.

Fig 13.1.2b Account names listed in the “Account Settings” dialog box.


2. DeliveryStore:
This property returns a ‘Store’ object. It represents a file (.ost or .pst) on the
local computer or a network drive. It stores all the data items for the
concerned account. The folders in an account have to be stored in a physical
file. That file is represented by the DeliveryStore for the account.
The delivery store for a particular account can be obtained as follows:
Dim olStore as Outlook.Store
Set olStore = olAcc.DeliveryStore

We will not use the delivery store extensively so we don’t need to assign it to
any object. Our only purpose would be to get access to the folders contained
in the store. We will use the syntax shown in the description of
GetDefaultFolder method of the Store object.
Store:
This object represents a physical file created on a computer’s filesystem that
holds all the data of an account. It may be on the local computer’s hard drive or
on some network computer.
We will not be using the Store object extensively. But we will need one method
of that object to access our Inbox. Hence we provide a brief description of the
object here.
Methods
1. GetDefaultFolder:
This method returns a default outlook folder of a specified type.
By specified type we mean the type of data that is held in the folder. For
example, Inbox for emails and Calendar for invites, meetings, appointments
and so on.
By a default-folder, we mean the folders that outlook creates for a particular
account. The user can make other folders for his/her own purpose. The syntax
for the method is:
Dim olFld As Outlook.Folder
Set olFld = <DeliveryStore>.GetDefaultFolder(olFolderType)

DeliveryStore is the Store object obtained using the ‘DeliveryStore’


property of the Account object.
Arguments:
A) olFolderType : is a predefined constant representing the Folder we want to
work with. Since we will be working only with Inbox the value of the
constant we use will be ‘olFolderInbox’. Some of the other folders are
olFolderSentMail, olFolderDrafts, olFolderContacts and so on.

B) olFld : is the Folder object set to the folder returned by the method.
When we don’t have a separate object for the delivery store we can use the
following syntax:
Dim olFld As Outlook.Folder
Set olFld = olAcc.DeliveryStore.GetDefaultFolder(olFolderType)

Folder:
This object represents a set of items of a particular type. Inbox is a folder that
holds mail items (objects of type MailItem). Contacts is a folder that holds
contact items.
We obtain a folder object using the ‘GetDefaultFolder’ method of a Store object.
Properties
1. Items:
This is a collection of all the items held in the folder. To get the total number
of items in the folder, we can use:
lNumItems = olFld.Items.Count

To get the nth email from the folder, we can use:


Dim olMail As Outlook.MailItem
Set olMail = olFld.Items(n)

Where olFld is the object that refers to the Inbox folder obtained using the
GetDefaultFolder method.
Methods
1. GetTable:
This method returns a Table object that represents (as a collection of rows) a
set of all the items contained inside a folder. The properties of each item in
the folder are represented by columns of the table.

The items contained in the Inbox are called MailItems. Each of these items
has some properties like the name of the sender, date of creation and a
subject.

When we call the GetTable method of the Inbox folder, we get a tabular
representation of all the mail items. Each email is a row in the table. One of
the columns will contain the time when the mail was created, and another one
will contain the Subject and so on.

Here is how we can use the method:
Dim olFld As Outlook.Folder
Dim olTblInbox as Outlook.Table
Set olFld = DeliveryStore.GetDefaultFolder(olFolderInbox)
Set olTblInbox = olFld.GetTable

It is not advisable to use this table right away as it will contain all the emails
contained in the folder. At the same time, it will not have all the columns that
we require.

A bit later, we will take a look at methods to restrict the rows contained in the
table to items that meet a specified criteria and also to add additional columns
to the table as per our requirements.
Table:
This object represents a set of items contained inside a folder in the form of rows
and columns. Each item becomes a row and each of its properties becomes a
column.
Once we have a table, we can visit each row and read data from it. We describe
this process in greater detail in the coming sections. However, there are two
special positions from where we cannot read data because they represent the
bounds of the table. The first point, is just before the first row. From this position
we can only move forward to the first row of the table. Another is the position
just after the last row. Also known as End of a table.
Properties
1. Columns:
This is a collection of all the columns held in a Table. As we mentioned, each
column in a table corresponds to one property of the items held by the Folder
underlying the concerned Table.

By default, Outlook provides only a small set of columns for every table. The
properties represented by those columns may not be enough to serve our
purpose.The following is a list of properties that Outlook provides, by default,
for the items in the Inbox folder:
a) EntryID: a unique ID for each Item in a delivery store. This property will
be useful to us in the future.
b) Subject: subject line of the email.
c) CreationTime: Not much useful as we are interested in the time when we
have received the email.
d) LastModificationTime
e) MessageClass

As we can see, not all the required properties are available. Usually we would
want to search emails using the sender’s name (or email) and the subject. If
we are creating periodic reports with inputs we receive from others, we would
want to restrict our search to items received on or after a particular date.

Fortunately, we can add certain non-default properties to the ‘Columns’
collection and then search items based on the values in those columns. The
regular ‘Add’ method of a collection comes in handy here. Here is the code
to add the columns for received time, sender’s email address and sender’s
name to the columns collection.
With olTblInbox.Columns
.Add ("SenderName")
.Add ("ReceivedTime")
.Add ("SenderEmailAddress")
End With


2. EndOfTable:
This is a Boolean value. A value of True indicates that the current reading
position is after the last row of the table. Any attempt to refer to a row beyond
this position will generate an error.
We will see an example where we use this property to loop through all the
rows of a table. We will keep looping till the value of this property changes to
True.
Methods
1. Restrict:
This method returns a Table object that represents a smaller set of the items
filtered out from an initial table based on certain criteria. The criteria are
specified with a Filter parameter using a string written in the Microsoft Jet
language.

Don’t worry, we won’t be getting into the nitty-gritty of the Jet language. We
will use only the most basic features.

We can avoid the use of filter altogether, only problem is that we would have
to check each and every row of the table, which is not very appealing. So let’s
take a look at methods for specifying filters for some basic properties.

We will use the following string variable for holding our filter criteria in all
our code examples:
Dim strFilter As String

a) Specifying a filter for ‘ReceivedTime’
We can specify a filter using ‘=’ operator for matching an exact date. The
syntax would be: (dates are specified im MM/DD/YYYY format
surrounded by single quotation marks)
strFilter = “[ReceivedTime]= ‘01/10/2008’”

We can filter emails received in a specific time period by using the ‘<=’
and ‘>=’ operators


strFilter = “[ReceivedTime] >= ‘01/10/2008’ AND ” & _
“[ReceivedTime] <= ‘02/28/2008’

b) Specifying a filter for ‘SenderName’
Please note that the sender name (John Doe in our example below) has to
exactly match the name as it appears in the Inbox in the ‘From’ column.

strFilter = “[SenderName] = ‘John Doe’”

We can search for more than one sender. The next example is for filtering
emails sent by John Doe or by Mary Jane.

strFilter = “[SenderName] = ‘John Doe’ OR ” & _
“[SenderName] = ‘Mary Jane’

c) Specifying a filter for SenderEmailAddress
Filtering by email address is a much better method because each email ID
is unique even if the persons have similar names.
Another advantage of using email address is that we don’t have to worry
about how the sender chooses to display his/her name in outlook. For
example, John Doe’s name might be displayed as ‘John D.’
strFilter = “[SenderEmailAddress] = ‘[email protected]
Filtering based on multiple columns:
If we want to specify filter criteria for multiple columns, we can simply
combine the criteria using logical operators ‘AND’ and ‘OR’.
For example, see the following syntax for both, SenderName and
ReceivedTime.
strFilter = “([SenderName] =’John Doe' Or “ & _
“[SenderName] ='mary jane') And “ & _
“([ReceivedTime] >= '02/02/2007' And “ & _
“[ReceivedTime] <= '10/1/2007')”

Few observations on the code shown above:


1. Notice how we have separated the criteria for the two columns using
parenthesis.
2. The names of columns have to be exactly as they are available in Outlook.
Notice the absence of space between words and also the capital letters.
3. The criteria themselves are not case sensitive. Example ‘mary jane’ and
‘Mary Jane’ would produce the same results.

Getting the filtered table:
Once we have constructed the filter string, we can fetch the new filtered table
using the Restrict method. Here is the syntax for doing that:
‘//** A table object to hold the new filtered table **//
Dim olTblFiltered As Outlook.Table
Set olTblFiltered = olTblInbox.Restrict(strFilter)

2. MoveToStart:
This method takes the current reading position to just before the first row of
the table. Syntax for the method is as follows:
olTblFiltered.MoveToStart
3. Sort:
This method can be used to sort a table based on values in a column. The
syntax for the method is as follows
<TableObject>.Sort SortProperty, Descending

Arguments:
A) SortProperty: This is a string containing the name of the column with
which we want to sort the table.
B) Descending: This is a Boolean value specifying whether the table should
be sorted in descending order (True).
For sorting our table based on “ReceivedTime”, we use the following syntax:
olTblFiltered.Sort "ReceivedTime", True

4. GetNextRow:
This method takes the current reading position to the next row of the table. If
the current read position is before the first row, then calling this method will
take the read position to the first row of the table.
Once the reading position is moved, this method returns a Row object
corresponding to the current row. We will see the details of Row object in a
bit.
Syntax for the method is as follows

Dim oRow as Outlook.Row

Set oRow = olFiltered.GetNextRow
Row:
This object represents a particular row in a table obtained from a folder. For a
table obtained from the Inbox folder, each row is a reference to one particular
email in the Inbox.
Once we receive a row, we can refer the value of a given property using the
following syntax:
<rowObject>(“property name”)
For example, using the oRow object from the previous description, we can get
the properties as follows:
‘//** Entry ID of the email **//
oRow(“EntryID”)
‘//** Subject of the email **//
oRow(“Subject”)
‘//** Email Id of the sender **//
oRow(“SenderEmailAddress”)

The following piece of code checks if the subject of the email contains the word
“Sales Report” in it:
If(InStr(1, oRow(“Subject”), “Sales Report”)>0) Then
‘//** work with the email here. **//
End If

MailItem:
This object is the actual email we are looking for. Every object stored in Inbox
folder is of type ‘MailItem’.
The Row object described in the previous section gives us a reference to this
mail item. But to work further with the email, we need to extract this underlying
object.
How? Well this is where the EntryID property comes in. Recall from the
description of the ‘NameSpace’ object that we had a method which took the
EntryID and returned the specific Outlook Item.
Yes, you are right. We will use the ‘GetItemFromID’ method of the NameSpace
object.
Here is the syntax:
Dim olMail As Outlook.MailItem
Set olMail = olNS.GetItemFromID(oRow("EntryID"))

Here olNS is the NameSpace object which is instantiated at the beginning of our
macro.
Now we have the actual email with which we can work. Since we have already
obtained the subject and sender details. The next important task is to get the
attachments contained in the email.
But before that, let’s look at some of the important properties and methods of the
email item. Many of these are used when creating new emails.
Please recall that when we want to create a new email, we need to use the
CreateItem method of the Application object. Just to refresh our memories, here
is the syntax:
Dim olMail as Outlook.MailItem
Set olMail = olApp.CreateItem(olMailItem)

Properties
1. Body:
This is a string that contains the message text contained in the email. This
text does not contain any special formatting that may be applied.
This property is useful when we are creating emails that we want to send.

Here is an example of how we can create a body. The entire text of the body
is stored in a string variable and then added to the MailItem. Here is the code
Dim strBody As String
strBody = "Hi team," & vbCrLf & "Please find attached" & _
"the weekly status report" & vbCrLf & "regards"
olMail.Body=strBody


2. Attachments:
This property returns a collection object containing all attachments in an
email.
When the email is first created this collection is empty and we can add files
to We discuss this object in more detail in an upcoming section. this
collection.

3. Recipients:
This property is a collection of Recipient objects. As the name suggests,
these are people or teams who would receive our email. We discuss this
object in more detail in an upcoming section.

4. SendUsingAccount:
When creating a new email, the CreateItem method was called on the
Application object. But if our profile has multiple accounts configured, how
will Outlook know which account to use when sending the email or when
saving the mail as draft?
That is where this property comes in. For every email, we can explicitly set
the Account object. Here is the syntax:
Dim olAcc Outlook.Account
Set olNS = olApp.GetNamespace("MAPI")
Set olAcc = olNS.Accounts(<Account Display Name>)
olMail.SendUsingAccount = olAcc

Here ’olApp’ is an Application object that is already instantiated, and ’olMail’
is a new MailItem.
Important: This property has to be set before we call any method to send or
save the new email.
Methods
1. Send:
This method sends our email to the intended recipients that were added using
the Recipients collection.
The mail is sent from the account set for the SendUsingAccount property. The
syntax is pretty straightforward:
olMail.Send
2. Save:
This method saves our email in the Draft folder of the account that is set for
the SendUsingAccount property. Here is the syntax:
olMail.Save

Attachments collection & Attachment object:
‘Attachments’ collection is a collection of all attachments in an email. It is
obtained using the ‘Attachments’ property of a MailItem object.
Each member in the collection is an object of type ‘Attachment’ which is another
object in the Outlook object hierarchy.
We will not look at the entire list of properties and methods of Attachment(s).
Instead we will focus on two important tasks.
a) Add an attachment to a new email.
To add an attachment to an email, we call the Add method on the
Attachments collection of the email. The Add method has the following
syntax:
olMail.Attachments.Add(Source, Type, Position, DisplayName)

Arguments:
1. Source: This is a string containing the full path of the file that we want to
attach in the email. This argument cannot be omitted.

2. Type: This is a long integer value specifying the form in which the
attachment will be added in the email.
Each value is represented by a named constant. The valid values are shown
in the table below. We will always use olByValue. This is an optional
argument and can be safely ignored.

Named
Value Description
Constant
The attachment is a copy of the original file and can
olByValue 1 be accessed even if the original file is moved form
its location.
The attachment is an Outlook message format file
olEmbeddeditem 5
(.msg) and is a copy of some original message.
The attachment is an OLE document. An embedded
olOLE 6
chart or Excel sheet

3. Position: This is a long integer value and dictates the position of the
attachment relative to the text in the email body. It applies to messages that
use Rich Text Formatting for the body text. This is an optional argument,
and we will not use it. Our emails will have simple text bodies with default
attachment positions.
4. DisplayName: This is a string value that allows us to specify an alternate
name of our attachments in case we don’t want to show our original file
name. It is an optional argument and we will not use it. Here is the code for
adding an Excel file to a new email:

olMail.Attachments.Add(“C:\statusreport\Sales_Jun2015.xlsx”)

b) Download an attachment from a received email.


For an email received in our inbox, we can get a collection of all the attached
files using the ‘Attachments’ property of the MailItem object.
The following points are applicable:
1. We can use the ‘Count’ property to know the total number of attachments
contained in the email.

2. An individual attachment can be found by using its index. For example, the
code below will give a reference to the third attachment.

olMail.Attachments(3)

We don’t have to worry about a specific index since we would usually be


interested with all the attachments. But we should note that, as with all
collections, the index for ‘Attachments’ collection starts with 1.
Every member of the Attachments collection is an ‘Attachment’ Object. We
will use exactly one property and one method of this object. These are:
1. FileName property: This is a string that holds the actual name of the file
that is attached. This comes in handy when we are downloading the
attachments. We will see a code example soon.

2. SaveAsFile method: This method allows us to save the attachment at a
path of our choice. File (having similar name) is overwritten if it already
exists.
And finally, here is the code that goes through all the attachments in a received
email (assigned to ’olMail’ object) and saves them to the folder ‘Input’ that is
in the parent folder containing the current workbook.
Dim strSavePath As String
Dim olAttach As Outlook.Attachment
strSavePath = ThisWorkbook.Path & "\Input\"
For i = 1 To olMail.Attachments.Count Step 1
Set olAttach = olMail.Attachments(i)
olAttach.SaveAsFile (strSavePath & olAttach.Filename)
Next i


Recipients collection & Recipient object:
‘Receipients’ is a collection of all the people or teams who will receive our
email. It is obtained by using the ‘Recipients’ property of a MailItem object.
Each member of the collection is an object of type ‘Recipient’. We will not dive
into all individual methods and properties of this object. Instead, we will look at
two important tasks we do when creating a new email from within our macro.
For the code below, we assume that we already have a newly created MailItem
object in the variable ’olMail’.
a) Add recipients to our email.
To add a new recipient, we use the ‘Add’ method of the Recipients collection.
This method takes a string argument. The string can be either the
DisplayName or the email of the intended recipient.
We should use the email address which will always be unique and will free us
with details of specific display names.
The ‘Add’ method returns a new Recipient object which we can then modify
to set the To/CC/BCC. Here is the syntax:
Dim olRcp As Outlook.Recipient
Set olRcp = olMail.Recipients.Add(“[email protected]”)

b) Classify the recipients into To, CC and BCC categories.


This is done by setting the Type property of the newly obtained Recipient
object.
The three valid values for Type property are ‘olTo’, ‘olCC’, and ‘olBCC’.
The syntax is as follows:
olRcp.Type = olCC

The string argument for the Add method can be held in a string variable or it
can be read from cells in worksheet.
Suppose the email IDs are held in cells B2 to B10 on a worksheet named
‘MailingList’ in the current workbook (Cells A2 to A10 obviously hold the
name of the people) and, from row number 5 onward the recipients are to be
marked in CC. We can use the following code:
Set wks = ThisWorkbook.Worksheets(“MailingLIst”)
For i = 2 to 10 step 1
Set olRCP = olMail.Recipients.Add(wks.Cells(i,2).vlaue)
If (i>=5)then
olRCP.Type=olCC
End If
Next i

13.1.3 Read emails and download attachments


We will now look at an example in which, from our inbox we filter a set of mails
based on date and sender name. Then we scan each mail for a particular word in
the subject. Based on that, we download the attached files. Here is how we will
proceed:
Sub OutlookTest()
Dim olApp As New Outlook.Application
Dim olNS As Outlook.Namespace
Dim olAcc As Outlook.Account
Dim olTblInbox As Outlook.Table
Dim oRow As Outlook.Row
Dim strFilter As String
Dim olFld As Outlook.Folder
Dim olMail As Outlook.MailItem
‘//** Get the namespace and account **//
Set olNS = olApp.GetNamespace("MAPI")
Set olAcc = olNS.Accounts("[email protected]")
If olAcc Is Nothing Then
‘//** If the account is not available, display a message to the user and stop **//
MsgBox "account not found"
Exit Sub
Else
‘//** If the account is found, get a reference to the Inbox folder **//
Set olFld = _
olAccG.DeliveryStore.GetDefaultFolder(olFolderInbox)


‘//**Get a reference to the table of the folder **//
Set olTblInbox = olFld.GetTable
‘//** Add more columns on which we will apply our filter **//
With olTblInbox.Columns
.Add ("SenderName")
.Add ("ReceivedTime")
.Add ("SenderEmailAddress")
End With

‘//** Compile the filter string **//
strFilter = _
“([SenderName] ='Doe John' “ & _
“or [SenderName] ='Elisha James') and " & _
“([ReceivedTime] >= '" & DateSerial(2016, 3, 1) & _
“' AND [ReceivedTime] <='" & DateSerial(2016, 4, 18) & "')"

‘//** Apply the filter to the Inbox table and get a new table. **//
Set olTblFiltered = olTblInbox.Restrict(strFilter)
‘//** Sort the new table as per the received date and time **//
olTblFiltered.Sort "ReceivedTime", True
‘//** Move to the beginning of the table (just before the first row) **//
olTblFiltered.MoveToStart
strSavePath = ThisWorkbook.Path & "\Input\"
‘//** Loop over all the rows of the table **//
While Not (olTblFiltered.EndOfTable)
Set oRow = olTblFiltered.GetNextRow()
Set olMail = olNS.GetItemFromID(oRow("EntryID"))
‘//** Download and save the attachments **//
For i = 1 To olMail.Attachments.Count Step 1
olMail.Attachments(i).SaveAsFile _
(strSavePath & olMail.Attachments(1).DisplayName)
Next i
Wend
End If
Set olTblInbox = Nothing
Set olAccG = Nothing
Set olNS = Nothing
olApp.Quit
Set olApp = Nothing
End Sub

In the ‘For’ loop we just assumed that there would be atlease one attachment. In
real cases, we should check the attachment count is greater than 0 before we
enter the loop.

13.1.4 Send emails with attachments


We will now look code that will create a mail object, put some text in the body,
attach two files and send it to three receipients, one of whom will be in CC.
Notice that here we are saving the email in the draft box, in case we want to
check it before sending. The alternative is to send it automatically. The
difference is in one single line of code and it has been shown in the comments.
The name of the receipients and path of attachments can be taken from cells of a
worksheet. In this illustration, we will write it directly into the code.
Sub mailsendtest()
Dim olApp As New Outlook.Application
Dim olNS As Outlook.Namespace
Dim olAccG As Outlook.Account
Dim olMail As Outlook.MailItem
Dim olRcp As Outlook.Recipient
Set olNS = olApp.GetNamespace("MAPI")
Set olAccG = olNS.Accounts("[email protected]")
Set olMail = olApp.CreateItem(olMailItem)
Dim strBody As String
strBody = "Hi team," & vbCrLf & _
"Please find attached the weekly status report" & _
vbCrLf & "regards"
With olMail
.Subject = "Current week status report"
.Recipients.Add ("[email protected]")
.Recipients.Add ("[email protected]")
Set olRcp = .Recipients.Add("[email protected]")
olRcp.Type = olCC
.Body = strBody
.Attachments.Add ("C:\reports\Status_22Apr2016.xlsx")
.Attachments.Add ("C:\reports\Status_22Apr2016.pptx")
‘//** Save the email in draft **//
.Save
'//** to send the email instead of saving use the following code **//
‘//.** SendUsingAccount = olAccG **//
End With
Set olMail = Nothing
Set olAccG = Nothing
Set olNS = Nothing
Set olApp = Nothing
End Sub

13.2 Working with Lotus Notes
Now we will look at automating another popular email client – IBM Lotus
Notes.
Before we can use the Lotus Notes objects in our macro, we need to add its
library into our project. This library gets installed on a computer when the Lotus
Notes client is installed.

13.2.1 Coupling Notes to Excel

In the Visual Basic Editor go to the menus: Tools -> References



In the box that comes up, scroll and select the ‘Lotus Domino Objects’ library
and click OK.

13.2.2 The Lotus Notes Object Model


Organization of a Lotus Notes file:
Lotus notes files (.nsf) files also called Databases are collections of entities
called documents. Each document has several properties associated with it.
For the user to work on, each document is put up in a ‘View’ where some of the
properties are displayed. Views are given names as per the functions a user can
perform on the documents in that view.
Views are mostly displayed in tabular form with each property displayed in a
column. Each document forms a row of a View.
An example of a View is the ‘Inbox’. Each email message is a document. The
usual columns that are displayed are: Sender’s Name, Subject, Size and
Date/Time when message was received.
Although there are many ways to access notes documents using code, Views
provide the most intuitive and quick way to work with documents. Hence, for
our purpose, of quickly reading and sending emails, we will be using Views to
access and work with individual documents.

Following is a list of the essential objects and their important properties and
methods required for working with lotus notes email.

NotesSession:
This is the top-most object of the Object model. It represents the launching of
Lotus notes application from the ‘Start’ menu in windows.
We have to provide our password for starting the session.
Since we would need only one session for our entire code, we can instantiate the
notes session at the time of declaration.
Dim ns As New NotesSession
Methods:
1. Initialize:
This method represents beginning of notes. If Notes is already running, the
method returns the running session, else it launches Notes. Password of the
current user ID needs to be provided.
We assume that the user executing the code has Notes configured with his/her
userID
ns.Initialize ("userPassword")
2. GetDatabase:
This method is used to instantiate a NotesDatabase object with a particular
.nsf file. The method takes 3 arguments. The syntax for the method is as
follows
(You as a user should have valid access to the database for this to work)
Session.GetDatabase(Server,NsfPath,CreateIfFail)

Arguments:
A) Server: This is a string containing the name of the server on which the nsf
file is located. If we are working with a replica or archive on our local disk
(same where the macro is running), we can specify an empty string (“”) for
the server parameter. Sample code is provided at the end of this method’s
description.

B) NsfPath: This is a string containing the path of the nsf file as located on
the server.

C) CreateIfFail: This parameter should always be ‘False’ else Notes will try
to create the database if it is not found (which is more likely due to
mistakes in servername and path)
Here is a sample code when the database is accessed on a server:
Dim ndb As NotesDatabase

Set ndb = ns.GetDatabase _
("fx23mcq/M/ORG", "mail/usr12.nsf", False)

We can lookup the server and path parameters in our Notes workspace as follows
(Refer to figure 13.2.2a, bottom half of the dialog box has been cropped):
· Right-click on the icon of the database in the notes workspace and selecting
“Application” -> “Properties”.
· In the box that comes up, select “Database” near the top-left. Read the
parameters directly.
· Alternatively, in place of server name we could put the IP of the server in
double quotes. Check with your system administrator for obtaining the IP.


Fig 13.2.2a Database properties dialog box

Here is a sample code when the database is an archive created on our harddisk:
Dim ndb As NotesDatabase
Set ndb = ns.GetDatabase("","archive/arch_2014.nsf", False)

Here is a sample code when the database is a replica created on our harddisk:
Dim ndb As NotesDatabase
Set ndb = ns.GetDatabase("", "mail/usr12.nsf ", False)

In case a valid database is not found, the NotesDatabase object (ndb) is set to
‘Nothing’. We should check for this before we proceed with our code.
If ndb is Nothing then
Msgbox (“No valid database found”)
Exit Sub
Else
‘//** code for working with the database goes here
**//
Endif


NotesDatabase:
This is the “.nsf” file where our emails reside. It is obtained using the
GetDatabase method described earlier.
Properties:
1. Views:
This property gives a collection of all the views for a database. The objects of
the collection are NotesView objects. This may be required when we want to
list out all the views of a database in case we don’t know the exact name of a
particular view which is required to work with documents. See the GetView
method below for an example.
The code shown here retrieves names (using the Name property of each view)
of all the views in a database and lists them in the intermediate window of the
code editor.
Dim vw As NotesView
For Each vw In ndb.Views

Debug.Print vw.Name & vbCrLf


Next vw

Methods:
1. GetView:
This method takes name of a view of a database and retrieves that view. The
returned view can be used to instantiate a NotesView object.
The code below retrieves the “Inbox” view. Note how the view’s name
contains a ‘$’ preceeding it. The name was obtained using the code given
above for the ‘Views’ property. Without the ‘$’ sign, the code would generate
an error.
Dim vw As NotesView

Set vw = ndb.GetView("$Inbox")

2. Replicate:
As the name suggests, the method replicates the database with its server copy.
The method returns True if replication process runs without error, else it
returns False. Syntax for the method is as follows
ndb.Replicate(ServerName)

Use this method immediately after the GetDatabase method to get the latest
server copy of the database. Check for successful replication – return value
should be ‘True’ . Exit the program if required.
If not(ndb.Replicate(“fx23mcq/M/ORG”)) then
Msgbox (“Replication failed”)
Exit sub
Else
‘//** code for working with the database goes here **//
Endif

3. CreateDocument:
As the name suggests, this method creates a document in a database. It returns
a NotesDocument object that represents the new document.

When working with emails, this produces the same effect as clicking “New
Memo” except that none of it is visible on the screen. This method takes no
argument.

Dim doc as NotesDocument
Set doc = ndb.CreateDocument

NotesView:
A view is a fundamental part of the database that we will be working with. A
view represents a collection of documents that users can see on their screens
when they open their Inbox or a Lotus Notes team room.
For emails, the Inbox is a fundamental view. Any folder created by a user is also
a View. A NotesView is obtained by using the GetView method of a given
database. Each document (in our case, an email message) is shown as a row in a
View. Each such row is called an Entry.
Collection of all rows is called an EntryCollection. It should be noted that these
rows by themselves are not the documents (or messages), but only visual
representation of some parts of the messages (Sender Name, Subject, Date etc.)
Properties
1. AllEntries:
This property gives a collection of all the entries in a given View. We can loop
through each entry one at a time. The property returns a
NotesViewEntryCollection object.

2. EntryCount:
This property gives the total number of entries in a view. For the Inbox, it is
the total number of messages (read and unread)

NotesViewEntryCollection:
This object is a collection of all the Entries present in a view. Although Lotus
Notes provides advanced methods for creating this collection, we will simply
focus on getting all the entries in a view and then looping through them.
Each member of the collection is a NotesViewEntey object.
Dim vw As NotesView
Dim vwEntColl As NotesViewEntryCollection
Dim vwEnt As NotesViewEntry
Set vw = ndb.GetView("$Inbox")
Set vwEntColl = vw.AllEntries
For Each vwEnt in vwEntColl
‘//** ------------------------------------------------------- **//
‘//** code for working with the database goes here **//
‘//** -------------------------------------------------------- **//
Next vwEnt

Properties
1. Count:
This property gives the total number of entries in a collection.
Methods
1. GetFirstEntry:
This method retrieves the very first entry in the view. The entry returned may
not be the topmost entry a user sees on the screen. This is because entries on
screen are sorted based on some column.
2. GetNextEntry:
This method takes one view entry as a parameter and returns the entry
immediately following it. If there are no more entries left in the view, it
returns ‘Nothing’.
The code below gives the main approach we will take when searching for
mails in an Inbox.
Dim vw As NotesView
Dim vwEntColl As NotesViewEntryCollection
Dim vwEnt As NotesViewEntry
Set vw = ndb.GetView("$Inbox")
Set vwEntColl = vw.AllEntries
Set vwEnt = vwEntColl.GetFirstEntry
While Not (vwEnt is Nothing)
‘//** -------------------------------------------------------- **//
‘//** code for working with the database goes here **//
‘//**-------------------------------------------------------- **//

‘//** Get the Entry immediately following the current entry held in
vwEnt **//
Set vwEnt = vwEntColl.GetNextEntry(vwEnt)
Wend

NotesViewEntry:
This object represents a row listed in a View. This object is obtained by using
one of the methods described earlier for NotesViewEntryCollection.
Properties:
1. ColumnValues:
This property gives a zero based array that contains the values for a particular
entry for each of the columns in the parent View.
For example, when the View is the Inbox, each entry is a row we see in the
inbox and the column values would be Sender’s Name, Subject, Date of
receipt, Size of the message and so on.
Not all the columns are visible in Lotus Notes. For example, there might be
columns between Sender’s Name and Subject columns that are not visible.
These may be related to things like Priority Flags, Availability on Sametime
messaging and so on.
Because of these hidden columns, it’s not possible to tell the exact position (or
array index) of a particular column that we are interested in (like Date, sender
or Subject) merely by looking at the Inbox screen.
A better way is to retrieve one entry from the view, and list out its column
values. The code below shows how to do this.
Dim vw As NotesView
Dim lColValUB As Long
Dim vwEntColl As NotesViewEntryCollection
Dim vwEnt As NotesViewEntry
Set vw = ndb.GetView("$Inbox")
Set vwEntColl = vw.AllEntries
Set vwEnt = vwEntColl.GetFirstEntry
lColValUB = vwEnt.ColumnValues
For i = 0 to lColValUB Step 1
Debug.Print vwEnt.ColumnValues(i)
Next i

Once the column locations are known, we can read the value at that location in
each row of the view and work with the rows that meet our requiremetns. The
upcoming code examples will make the clarify the technique.
2. Document:
Finally, we reach our email message. The ‘Document’ property of a
NotesEntry object gives us the actual document related to a given Entry.
Through this object we gain access to the body of the message (the text
written therein) and any attachments. We also get access to details about the
sender, subject etc, but as noted earlier we will take these details from the
Entry related to a document.
NotesDocument:
This object represents the actual content stored in the “.nsf” file. For our
purpose, this content is an email message.
Within the email message itself there are several parts. We will deal with the
main content of the message – what the sender has written and all the files that
are attached.
Properties
1. ColumnValues:
This property returns an array of values in the same order as they appear in the
document’sparent view.

The first value in the array is the value that appears in the view’s first column
for thedocument, the second value is the one that appears in the second
column, and so on.

Methods
1. Send:
This method sends a document by email. The syntax is as follows:

doc.Send(attachForm [, recipients ])

The first argument is a Boolean value that we set to false for emails. The
second argument is an array of strings that contains names of recipients of our
email. We ignore this argument and set the recipient email IDs with a different
method.
2. Save:
This method saves any changes you we have made to a document. The syntax
is as follows:

doc.Save(force , createResponse [, markRead ] )

All three arguments are of type Boolean and the third argument is optional.
These are not important when working with email. Hence we can specify
‘False’ for both the required arguments. So for a NotesDocument that is an
email that we have composed, we can write:
doc.Save False, False

3. ReplaceItemValue:
This method finds each item in the document that has a specified name and,
replaces each one new item, and then assigns the specified value to that new
item. If the document does not contain an item with the specified name, the
method creates a new item and adds it to the document.

This method comes in handy when composing emails. It allows us to add
values to fields like Subject, Emails of recipients etc.

The first line of code makes the document of type “Memo” or email (Lotus
notes can have many different ‘Types’ of documents). The next two lines add
the list of recipients and the mail subject.

doc.ReplaceItemValue "Form", "Memo"
doc.ReplaceItemValue "SendTo", "[email protected]"
doc.ReplaceItemValue "Subject", "test mail"

4. CreateRichTextItem:
Creates a new rich text item in a document, using a name we specify, and
returns the correspondingNotesRichTextItem object.

This is an indispensable method because the body of the email is an object of
type ‘NotesRichTextItem’ and contains any files attached in the email.

The code below creates a NotesRichTextItem named “Body” inside a
document. This code is useful at the time of composing a mail that we want to
send.

Dim rt As NotesRichTextItem

Set rt = doc.CreateRichTextItem("Body")

5. GetFirstItem:
This method takes a name and returns the first item in the document that
matches that name.

Beware that a mail document can have several items with the same name. This
method will return the first one. An example would be a mail that has been
forwarded multiple times or a mail having several sections. Such a mail can
have multiple items named “Body”.

When we describe the NotesItem object we will see a procedure that can be
used to work with multiple items with the same name. For now, since we are
concerned with getting input files attached in emails, we will assume that files
we are concerned with are in the first “Body” item.

The code below retrieves an item named “Body” from inside a document and
assigns it to a NotesRichTextItem object. This code is useful at the time of
reading a mail.
Dim rt As NotesRichTextItem

Set rt = doc.GetFirstItem("Body")
NotesItem:
A NotesItem object represents a specific piece of data contained within a
document.
Items are given names and our program can access individual items using their
names. However as mentioned earlier, a document can have multiple items with
the same name.

§ In case we want to process multiple items with the same name, we can get the
first item, assign it to a ’NotesItem’ object, process it, remove it using the
’Remove’ method of the ’NotesItem’ object, and then use the ’GetFirstItem’
method to get the next item of the same name.



Properties
1. Text:
This property returns a string with a plain text representation of a value held in
an item.
Methods
1. Remove:
This method deletes an item from a document. In order for the change to
become permanent, we have to call the ‘Save’ method of the document.
However, if we are only reading a mail document, we don’t need to save the
changes.

NotesRichTextItem:
This object represents the visual content we see in the email messages. This
object along with its child objects contains all the items like text, tables, tabs, file
attachments and collapsible sections that would exist within a document.
We will be mainly dealing with the email text and attached files.
Properties
1. EmbeddedObjects:
This property returns an array containing all the embedded objects, object
links, and file attachments contained in a rich text item. Members of the array
are objects of NotesEmbeddedObject class.
Methods
1. AppendText:
This method inserts text in a rich text item. We use this method at the time of
composing mails that we want to send out.
rt.AppendText(“Hello Team,”)

If we have the message stored in a string variable, we can use the method as
follows:

Dim strMsg as string

strMsg = “Please find attached compiled sales report”
rt.AppendText(strMsg)

2. AddNewLine:
Inserts a new line in the RichText item

rt.AddNewLine


NotesEmbeddedObject:
This object represents an individual embedded link or file attachment.
Properties
1. Type:
This property returns a long integer value representing the type of embedded
object.
Why is this important for us? A mail message can have several embedded
objects in the form of files as well as links. We are interested only in file
attachments, so before we go ahead with processing an embedded object we
need to ascertain its type.
For attached files, the value of Type property could be ‘1084’. However, we
will not rely too much on a numeric value. Instead we will rely on a defined
constant ‘EMBED_ATTACHMENT‘. So, to check if an object ’obj’ is a file
attachment, we use the code below:
If (obj.Type = EMBED_ATTACHMENT) then
‘//** Code to work with the attached file. **//
EndIf

2. Source:
This property returns a string value that represents:
§ Internal name that Notes uses to refer to the source document in case of
linked objects (we will not discuss these any further)
§ File name of the original file in case of File Attachments.

This property is most useful for checking the extension of multiple attached
files. In case our mails are expected to have multiple attached files like pdf,
text and Excel. We can check the extension of each file and download only the
relevant ones.
Methods
1. ExtractFile:
This method saves a copy of an attached file to the hard disk. Syntax for this
method is a follows:
obj.ExtractFile(strPath)

The argument strPath is a string containing the path where the extracted file
will be stored. It should include the name of the file. Since we are interested in
saving the files with the same names as in the email, we will compile the value
of the path. The following line of code shows how
strPath = “C:\Reports\” & obj.Source

13.2.3 Code for downloading attached Excel files


We now look at a complete code listing for reading files from a Lotus Notes
inbox, picking emails with a certain subject and that were received after a certain
date, then downloading all the Excel files attached in the email.
For the purpose of this example, consider that:
1. Column 3 of the Inbox view gives the name of the sender.
2. Column 6 holds the subject.
3. Column 7 holds the date when mail is received.
4. We request our colleagues to include the words “sales input” in the subject
line and they agree.
5. Schedule of reporting is such that we need to process only those input files
that have been received since the day before.

Sub GetAttachedFiles()
Dim ns As New NotesSession
Dim ndb As NotesDatabase
Dim vw As NotesView
Dim vwEntColl As NotesViewEntryCollection
Dim vwEnt As NotesViewEntry
Dim doc As NotesDocument
Dim strSubject As String
Dim strAttachName As String
ns.Initialize (“password”)
Set ndb = ns.GetDatabase("server", "filepath", False)
Set vw = ndb.GetView("$Inbox")
If vw is Nothing then

Msgbox (“Inbox could not be retrieved”)
Exit Sub
Else
Set vwEntColl = vw.AllEntries
Set vwEnt = vwEntColl.GetFirstEntry
‘//** Loop over all entries **//
While not (vwEnt Is Nothing)
strSubject = vwEnt.ColumnValues(6)



If (vwEnt.ColumnValues(7)>= (Date-2)) And _
(InStr(1, “sales input”, strSubject)>0) Then
‘//** If the email was received in the past 2 days and **//
‘//** its subject contains ‘Sales input’, process it **//
Set doc = vwEnt.Document
Set rtitem = doc.GetFirstItem("Body")
If (rtitem.Type = RICHTEXT) Then
For Each embObj In rtitem.EmbeddedObjects
If (embObj.Type = EMBED_ATTACHMENT) Then
strAttachName = Trim(embObj.Source)
If InStr(1, “.xlsx”, strAttachName))>0 Then
‘//** Download only if the file has .xlsx extension **//
embObj.ExtractFile _
("c:\Reports\" & strAttachName)
End If
End If
Next embObj
End If
End If

‘//** Move to the next entry **//
Set vwEnt = vwEntColl.GetNextEntry
Wend
Endif
Set doc = Nothing
Set vwEntColl = Nothing
Set vw = Nothing
Set ns = Nothing
End Sub

13.2.4 Code for sending mails with attached Excel


files
We now look at a complete code listing for reading files from a Lotus Notes
inbox, picking emails with a certain subject and that were received after a certain
date, then downloading all the Excel files attached in the email.
The code below creates a new email, puts in some text, adds a file attachment
and sends it to a set of people, two in this case.
We have added the recipient information directly in the code. However, these
can be read from cells on a worksheet.
Sub SendMailWithFiles()
Dim ns As New NotesSession
Dim ndb As NotesDatabase
Dim doc As NotesDocument
Dim rt As NotesRichTextItem
‘//** String array of 2 elements to hold recipient email IDs **//
Dim strRecipient(2) As String
strRecipient(0) = "[email protected]"
strRecipient(1) = "[email protected]"
ns.Initialize (“password”)
Set ndb = ns.GetDatabase("server", "filepath", False)
Set doc = ndb.CreateDocument()
doc.ReplaceItemValue "Form", Memo"
doc.ReplaceItemValue "SendTo", strRecipient
doc.ReplaceItemValue "CopyTo", "[email protected]"
doc.ReplaceItemValue "Subject", "test mail2"
Set rt = doc.CreateRichTextItem("Body")
rt.AppendText ("Hi Team,")
rt.AddNewLine
rt.AppendText ("This is a test mail" & vbCrLf & vbCrLf)
rt.EmbedObject EMBED_ATTACHMENT, _
"","C:\Reports\SalesData_Jun2015.xlsx"
doc.Send (False)
Set rt = Nothing
Set doc = Nothing
Set ns = Nothing
End Sub

13.3 Working with Gmail
In this section we will look at how we can automate reading and sending emails
from our Gmail account. There are multiple approaches and advanced
programmers would like to use the APIs provided by Google. Since we are
trying to to find a simple solution using Excel macros, we will try and do the
automation via Outlook.
It should be noted that the steps described here are also available in the Gmail
page for account settings. The instructions are current as of May 2016. Reader is
requested to check the link on the accounts page for the latest instructions.
We will first look at changes we need to make in our gmail account so we can
use it with outlook. Then we will see how we can setup outlook to work with
gmail.

13.3.1 Change Gmail settings to work with Outlook


Part I
Here we will see the basic steps we need to perform in Gmail

1. Sign in to Gmail.
2. Click the gear in the top right .
3. Select “Settings”.
4. Click “Forwarding and POP/IMAP”.
5. Scroll down till the section “IMAP Access” is visible on the left side
6. Select Enable IMAP.
7. Click Save Changes
Fig 13.3.1a Changing account settings in Gmail

Part II
It may happen that even after changing all settings as shown in Part I and
following the steps of section 13.3.2, Outlook may not be able to connect to
Gmail. In that case, we might have to make some additional changes to our
Gmail account.

1. In the Settings page, click on “Accounts & Import” link.
2. In the “Change account settings” section, click on the link “Other Google
Account Settings”. This opens the “My Account” page.

Fig 13.3.1b Changing account settings in Gmail


3. In the My Account page, click on “Sign-in & Security” link on the left hand
side.
4. On the right hand side, scroll down till the section marked “Allow less secure
apps”
5. Turn on this feature.

Fig 13.3.1c Changing account settings in Gmail

13.3.2 Setup Outlook to work with Gmail


Go to “File” menu and in the left hand side select “Info”
On the right hand side, in the “Account Information” screen, click on “Account
Settings”.

Fig 13.3.2a Account Information screen in outlook



This brings up the Account Settings dialog box shown marked with (1) in figure
13.3.2b
Fig 13.3.2b Account Settings (1) and Add Account (3) dialog boxes

In the Account Settings dialog box, click on the “New” button shown marked
with (2) in figure 13.3.2b. This will bring up the Add Account dialog box shown
marked with (3).
In the the Add Account dialog box select the radio button marked “Manual setup
or additional server types” marked with (4) and click on “Next”. This will take
us to the “Choose Service” screen of the same dialog. Part of this screen is
shown in figure 13.3.2c
On the “Choose Service” screen, select the “Pop or IMAP” option and click on
“Next”. This takes us to the “Pop or IMAP Account settings” screen shown in
figure 13.3.2d.
Fig 13.3.2c Choose Service screen

In the “Pop or IMAP Account settings” screen, please ensure the following:
· In the “Email Address” and “User Name” fields we need to enter the full
email ID including the “@gmail.com” part.
· Ensure that “IMAP” is selected in the “Account Type” dropdown.
· Incoming mail server should be: imap.gmail.com
· Outgoing mail server should be: smtp.gmail.com

Fig 13.3.2d Choose Service screen

Once the fields are filled up, click on “More Settings…” button marked with (1)
in figure 13.3.2d. This brings up the “Internet E-mail Settings” dialog box shown
in figure 13.3.2e.

Fig 13.3.2e Internet Email Settings – Outgoing Server

In this dialog, go to the “Outgoing Server” tab and check the box labelled “My
outgoing server(SMTP) requires authentication”.
Select the “Log on using” option. In “User Name” box enter the full email ID.
Then, enter the gmail password.
Next, in the “Internet E-mail Settings” dialog box, go to the “Advanced” tab as
shown figure 13.3.2e. In this dialog, make the following entries:
In the dropdowns labelled “Use following type of encrypted connection:”, Select
“SSL” and enter the following

Fig 13.3.2f Internet Email Settings – Advanced Settings

Server Port Numbers:
· Incoming Server: 993
· Outgoing Server: 465

Once all values are entered, click on “OK”.

This should take us back to the “Pop or IMAP Account settings” screen of figure
13.3.2c. Now click on “Next”.

Outlook will test the connection by logging in to our Gmail account and then
sending a test mail.

And we are done !! We can use the techniques described in the earlier sections.

If the test is unsuccessful, ensure that all entries are correct and perform the steps
in second part of section 13.3.1.

Chapter 14
Managing files on disk
In this chapter, first we will discuss automation of tasks that deal with managing
(copying, moving, deleting and getting a list of) files residing on discs. By discs
we mean not just the computer’s harddisk but also any mapped drives and any
external storage media connected to our computer.
14.1 Manage files with VBA - FileSystemObject
One common task in automating Excel is to manage files stored on our harddisk.
Microsoft provides a very handy object for this purpose. It is called the
‘FileSystemObject’.

As far as Excel VBA is concerned, the most important task done using this
object is to get a list of all files and folders residing under a given folder path.
Once we get the full path of the file, we can use the methods of specific
applications (Word, PowerPoint, Lotus Notes and so on) to open, read and work
with the file.

Before we can use the FileSystemObject we need to add its library into our
project. Here’s how:

§ In the Visual Basic Editor go to the menus: Tools -> References

§ In the dialog box that comes up, scroll and select the ‘Microsoft Scripting
Runtime’ library and click OK.

14.1.1 FileSystemObject
This is the top level object representing the file system (collection of all drives
and the folders and files on a computer - the one on which the macro is running).

As we need only one object our macro, we can instantiate the object at the time
of its declaration.
Dim fso as New Scripting.FileSystemObject
For the code examples in this section, assume that we have:
§ A folder named “Reports” in our “C:” drive.

§ Subfolders named “Sales”, “Inventory”, inside Reports folder

§ Sales folder has 12 Excel files named “SalesReport_Jan2015.xlsx” to
“SalesReport_Dec2015.xlsx”.

§ Inventory folder 12 excel files named “InventoryReport_Jan2015.xlsx” to
“InventoryReport_Dec2015.xlsx”.

Properties
1. Drives: This is a collection of all the drives on the computer on which our
macro is running.
Methods
1. GetFolder: Takes a path to a folder as argument and returns a folder object.
The syntax for this method is:
Dim fld As Folder

Set fld = fso.GetFolder(strFolderPath)



Arguments:
strFolderPath: This is a string containing the full path of the required folder.
It can be a string constant enclosed in double quotes or a string variable
holding the value of the path.
2. GetFile: Takes a path to a file as argument and returns a File object. Syntax
for this method is:
Dim f As File
Set f = fso.GetFile(strFilePath)

Arguments:
strFilePath: This is a string containing the full path of the required file. It can
be a string constant in double quotes or a string variable holding the value of
the path.
3. FileExists: This method checks if a particular specified file exists on the
disk. If it does, the method returns ‘True’ else it returns ‘False’.

This method should always be used before we open any file. If the file we
want to work with does not exist and, we don’t check for its existence, then
our macro may hang up with an error.

If this method returns ‘True’, we can proceed to open and work with the file
or if the method returns ‘False’, we can either create a new file or move on to
another file or display a message to the user. Syntax for this method is as
follows:

fso.FileExists(strFilePath)

As before, ’strFilePath’ is a string containing the full path of the required file.
4. FolderExists: This method checks if a particular specified file exists on the
disk. If it does, the method returns a ‘True’ else it returns ‘False’. The syntax
is as follows:
fso.FolderExists(strFolderPath)

As before, ’strFolderPath’ is a string containing the full path of the required
file.

5. CopyFile: This method copies one or more files from one location to
another. To move more than one file, we have to use wildcard characters ( *
and ?). The syntax is:

<filesystemobject>.CopyFilesource, destination, overwrite

Arguments:
a) source: This is a string containing the full path of the file to be copied. Or
a string containing wildcards if more than one or more files have to be
copied.

b) destination: This is a string value. Wildcards are not allowed in this string.
For example:
To copy all the Excel files with names starting with ‘Sales’, to the folder
‘Sales Reports’ folder, the syntax would be
fso.CopyFile “C:\Reports\Sales*.xlsx“,“C:\Sales Reports\“

To copy all the Excel files with names starting with ‘Sales_’ and having
any two characters followed by ‘2015’, to “Sales” folder, the syntax is
fso.CopyFile “C:\Sales_??2015.xlsx” ,“C:\Sales Reports\“

c) overwrite: This is a Boolean value. If set to True (the default value), any
existing files are overwritten. If set to False, files are not overwritten.

Notes:
1. If either ’source’ or ’destination’ does not exist, an error occurs. The macro
halts when the first error is encountered. No attempt is made to undo any
action that was taken
2. If there is a file that is marked as read only and, we are try to overwrite it,
an error occurs irrespective of the value of ’overwrite’.
3. Wildcards can occur only in the last portion of the ’source’ argument. For
example, the following value of source would cause an error:

“C:\*\Sales.xlsx“

6. CopyFolder: This method copies one or more folders from one location to
another. To move more than one folder, we have to use wildcard characters (
* and ?)

<filesystemobject>.CopyFoldersource, destination, overwrite

Arguments:
a) source: This is a string containing the path of one or more folders to be
copied. This string can contain wildcards but only in the last portion of the
path.

b) destination: This is a string value. Wildcards are not allowed in this string.

For example:
To copy all the folders with names ending with ‘Sales’, to the folder ‘Old
Reports’ the syntax would be

fso.CopyFolder “C:\Reports\*Sales”,“C:\Old Reports\”

To copy all the folders with names starting with ‘Sales_’ and having any
two characters followed by ‘2015’, syntax would be

fso.CopyFolder “C:\Reports\Sales_??2015”,“C:\Old Reports\”

c) overwrite: This is a Boolean value. If set to True (the default value), any
existing folders are overwritten. If set to False, folders are not overwritten.
Notes:
1. If either ’source’ or ’destination’ does not exist, an error occurs. The macro
halts when the first error is encountered. No attempt is made to undo any
action that was taken
2. The files and sub-folders within the source folder are also copied.
3. If ’destination’ is a read-only folder, or there is a read only file having the
same name as one of the copied files, an error occurs irrespective of the
value of ’overwrite’ argument.
4. Wildcards can occur only in the last portion of the ’source’ argument. For
example, the following value of ’source’ would cause an error:

“C:\*\Sales“


7. MoveFile & MoveFolder: These methods are similar to CopyFile and
CopyFolder methods except that the source files are deleted from the original
location after creating the copies. The syntax for these methods are:

<filesystemobject>.MoveFolder source, destination

<filesystemobject>.MoveFile source, destination

8. DeleteFile & DeleteFolder: These methods are used to delete specific file or
folder. The syntax for these methods are as follows:

<filesystemobject>.DeleteFolder strFolderPath, bForce

<filesystemobject>.DeleteFile strFilePath, bForce

The argument ’bForce’ is a Boolean argument with default value FALSE. If
set to TRUE, the methods will delete even those files or folders that are
marked as Read-only.

The ’strFolderPath’ and ’strFilePath’ are string arguments that hold the path
of the folder or file to be deleted.

An error occurs if the specified file or folder does not exist. Hence it is better
to check their existence by first using the ‘FolderExists’ or ‘FileExists’
methods.

9. CreateTextFile, OpenTextFile: These methods are used for creating new
text files and for working with existing text files. We will look into their
details in section 14.3 “Reading and Writing Text files”, where we will see
methods for working with text files.

14.1.2 Folder
Represents a particular folder on our computer. It is obtained by using the
‘GetFolder’ method of the FileSystemObject
Dim fld as Folder
Set fld = fso.GetFolder(“C:\Reports”)
Properties:
1. Name: This is a string value representing the name of a particular Folder. We
can read the name of a folder as well as change it.
‘//** Change name of Folder to “Report10” **//
fld.Name = “Report10”

2. Subfolders: This is a collection of all folders inside a folder. Using this


property gives a ‘Folders’ collection which is described next.

3. Files: This is a collection of all files inside a folder. Using this property gives
a ‘Files’ collection which is described next.

Dim fColl As Files
Set fColl = fld.Files
For Each f In fColl
‘//** Print the name of each file **//
Debug.Print f.Name
Next

14.1.3 Folders
This object is a collection of all folders inside a folder or drive. We obtain this
collection by using the SubFolders property of the Folder object.
We don’t need to set this to any object. We can use a For Each…Next loop to
work on each of the folders.
Dim fldColl As Folders
Set fldColl = fld.Subfolders
For Each f1 In fldColl
‘//** print the name of each folder **//
Debug.Print f.Name
Next

14.1.4 File
This object represents an individual file on the disk. Up to this point the type of
file does not matter. The file object just gives us a reference to the file on the
disk. In order to open the file, we need to launch the appropriate application. For
example, to open an Excel file, we need to launch Excel. A file object can be
instantiated in two ways:
1. Using the GetFile method of the FileSystemObject
Dim f as File
Set f = fso.GetFile(“C:\Reports\SalesReport_Jun2015.xlsx”)

2. Using the Files collection of a Folder Object
We can access the files one at a time using the code shown in the Section 14.1.2
(see the code given for ‘Files’ property)
Properties
1. Name: This is a string value representing the name of a particular File. We
can read the name of a file as well as change it.
The code below changes the name of the file we got in the code example
above.
f.Name = “SalesReport_June2015.xlsx”

Note that the file should not be open when we are trying to change its name.
2. Path: This is a string value representing the path of a particular File.
Methods
1. OpenAsTextStream: If the file object refers to a text file, then this method
can be used for opening and working that file. We will see more details in
section 14.3, “Reading and Writing Text files”, where we will see methods for
working with text files.
14.2 Let user select file(s) or folder(s) – FileDialog
Problem 1:
When processing files in bulk, our macro could not successfully process one or
more file. Now we need to execute the macro for only those files, so we need the
user to select the files and provide the location for our macro.
Problem 2:
Our input files are not consistently named, so we cannot program their names
into our macro (like SalesInput_<Region>.xlsx). We want the user to select the
files manually.
Problem 3:
We extracted our input files into a new folder. Now we need to select that folder
into our macro for processing.
Solution:
Use the FileDialog Object. In Excel VBA, this object is returned by FileDialog
property of Application object.
This object allows a user to select a single folder or, one or more files and
returns the paths of the selected objects in as collection.
We can use these paths with the FileSystemObject object described earlier, or if
the files selected are Excel files, we can use the paths as input to
Workbooks.Open method one by one.
The type of dialog, meaning whether it will pick files or folders, has to be
specified at the time of creating the object. The code below is used for creating
the dialog. Please note that creating a dialog does not display it on the screen. To
display the dialog, we have to use the ‘Show’ method described later.
Dim dlgFilePick As FileDialog
Set dlgFilePick = Application.FileDialog(dialogType)

The ’dialogType’ parameter takes one of four different values, out of which 2 are
important, these are:
· msoFileDialogFilePicker Allows user to select one or more
file(s).
· msoFileDialogFolderPicker Allows user to select a folder
We now list down the most important properties and methods of this object.
Properties (refer to figure 14.2 for details)
1. Title: This is a string value used to identify the dialog. It appears at the top of
the dialog’s window.

2. Filters: This is a collection representing the types of files that will be
available for selection. Filters are applicable only during File selection.

Each filter consists of two parts. First is a “Description”, a string that indicates
the type of files like, text, images, spreadsheet and so on. The second part
contains a string with the concerned file extensions separated by commas.
Each extension is prefixed with the wildcard character (*) to display all the
files of that particular type.

Filters appear as a dropdown at the bottom right of the dialog where the user
can select the type of files that will be displayed at a given time.

Filters are added to the collection using the Add method. The code below adds
two filters for the dialog. One for Text files and one for images.

dlgFilePick.Filters.Add "Text", "*.txt,*.rtf"

dlgFilePick.Filters.Add "Images", "*.jpg,*.png,*.gif"

In figure figure 14.2 we can see how the Title and Filters appear when the
dialog box is displayed.
Fig 14.2 A file dialog that prompts the user to select a file

Note that the “Type” property is set to msoFileDialogFilePicker but the
title has been set to “Select Input Folder”. This shows that the two
properties can be set independently of each other. At the time of writing
the macro we need to ensure the logical correctness.

If we want to use the same dialog box again, with a different set of filters,
we have to remove the existing filters first. This is done using the ’Clear’
method of the collection. Code below illustrates this:

dlgFilePick.Filters.Clear


3. AllowMultiSelect: This is a Boolean value and is used when the dialog is
used for selecting files.
Setting this property to True allows the user to select more than one file by
holding down the Shift or Ctrl key when clicking the file names.

4. SelectedItems: This is a collection having paths of all the selected Files (or
the single selected Folder). This collection should be accessed only after the
Show method has been executed.
The total number of paths is obtained by using the ’Count’ property of this
collection.
A particular path is obtained by using ’Items(intIndex)’ property. The code
below prints the path of each selected file into the Immediate window.
For i= 1 to dlgfilepick.SelectedItems.count step 1
Debug.print dlgfilepick.SelectedItems.Items(i)
Next i

If AllowMultiSelect is set to False, the path of one selected file is still returned
as a collection and it has only one item. In that case, the path can be obtained
by the code shown below:
dlgfilepick.SelectedItems.Items(1)
Once a path is obtained, we can use it as inputs for methods of
FileSystemObject and its child objects.
For example, if a folder is selected:
Set fld = fso.GetFolder _
(dlgFilePick.SelectedItems.Items(1))

If a file is selected:
Set f = fso.GetFile _
(dlgFilePick.SelectedItems.Items(i))

If the selected file is an Excel file:


Set Wbk = Workbooks.Open _
(dlgFilePick.SelectedItems.Items(i)))


5. InitialFileName: This property can be used to set the initial location that will
be opened and shown to the user. It is provided as a string input.
If we specify only a folder, then the last character of the string should be “\”
We can specify a specific file name with its full path. For example,
dlgFilePick.InitialFileName = _
"C:\Reports\SalesInput_June.xls"

This will open the file open dialog and navigate to “C:\Reports”. The file
name ‘SalesInput_June.xls’ is visible in the “File Name” box. However, it is
always better to provide a folder path even if we are trying to select files.
Hence we would set the property value as:
dlgFilePick.InitialFileName = "C:\Reports\”

In case our input files or folder are located within the folder that has the
current workbook (the one containing our macro), we can set this property
very easily as follows:

dlgFilePick.InitialFileName = ThisWorkbook.Path & “\”

Notice that the ‘Path’ property of workbook object does not append the “\” so
we need to put that in.
Methods
1. Show: This method displays the FileDialog on the screen. Execution of our
macro pauses till the user clicks one of the buttons.

The method returns a long integer value indicating the action taken by the
user.
a) If, after selecting files/folder, the user clicks “Open” the value (-1) is
returned
b) If the user clicks “Cancel” the value (0) is returned.
We provide below code for two subroutines that use FileDialog object for
working with Files and Folder.
Code for using FileDialog to pick files:
Sub FileDialogTest()
Dim lResponse As Long
Dim fso as New FileSystemObject
Dim dlgFilePick As FileDialog
Set dlgFilePick = _
Application.FileDialog(msoFileDialogFilePicker)
With dlgFilePick
.Title = "Select Input File(s)"
.AllowMultiSelect = True
.InitialFileName = "C:\Reports"
.Filters.Clear
.Filters.Add "Text", "*.txt,*.rtf"
.Filters.Add "Excel", "*.xls,*.xlsm,*.xlsx"
End With
lResponse = dlgFilePick.Show
If lResponse <> -1 Then
MsgBox "Please select one or more files"
Exit Sub
Else
For i = 1 To dlgFilePick.SelectedItems.Count step 1
‘//** Work with the files here **//
Next i
End If
End Sub





Code for using FileDialog to pick a folder:
Sub FolderDialogTest()
Dim lResponse As Long
Dim fso as New FileSystemObject
Dim fld as Folder
Dim dlgFilePick As FileDialog
Set dlgFilePick = _
Application.FileDialog(msoFileDialogFolderPicker)
With dlgFilePick
.Title = "Select Input Folder
.InitialFileName = "C:\Reports"
End With
lResponse = dlgFilePick.Show
If lResponse <> -1Then
MsgBox "Please select a Folder"
Exit Sub
Else
Set Fld = _ fso.GetFolder(dlgFilePick.SelectedItems.Items(1)
‘//** Work folder and the files therein **//
End If
End Sub


14.3 Reading & Writing Text files – TextStream object
The Microsoft Scripting Runtime library provides us a very efficient way to read
from and write to text files.
Towards the end of this section, we will see two code listings. One for reading a
delimited file one line at a time and transferring the text to a worksheet. Another
for taking data from a worksheet, and writing it to a text file in a particular
format.
To work with a text file, first we need to have an object attached to it. This object
is called a TextStream and is part of the Microsoft Scripting Runtime library.
Let’s look at the details.
This object allows us to access a text file sequentially. Here is the code for
declaration of two objects that are always required when working with text files.

Dim fso as New Scripting.FileSystemObject
Dim ts as TextStream



Ø What does sequentially mean? Is there any other type of access?
Sequential file access means that to reach a particular point (character) in the
file we have to read each and every character up to that character.

If our file has several lines of data each of which represents a record, then to
retrieve one particular record we have to read each of the records preceding
that record. Even if skipping a line is allowed, we need to first read that line
and then discard its contents.

This differs considerably from random access where we can specifically
instruct the program to position itself in the file after a certain number of
bytes or characters.

Ø How does our program know about the position of the character or line?
Each TextStream object has a property called the file pointer. We can think of
this as a bookmark which marks the location inside the file from where the
TextStream will allow reading or writing.

Any attempt to read data will return data from the location of the file pointer.
Any data written to the file will be inserted after the location of the file
pointer.

14.3.1 Ways to obtain a TextStreamObject


Following are the ways to obtain a TextStream object in our macro. Take special
note that the first two techniques involve using methods of the FileSystemObject
and the last one uses method of the File object

a) CreateTextFile method

This method creates a text file and then returns a TextStream object referring
to that file. We can use the new TextStream object to read from or write to the
file. The syntax is
Set ts = fso.CreateTextFile(FileName[,OverWrite[, Unicode]])

Arguments:
1. FileName: This is a string containing the full path of the file that is to be
created. It a can be the path enclosed in quotes (“ ”) or a string variable.

2. OverWrite: This is argument takes a Boolean value. If set to true, any
existing file is overwritten. The default value is False.
If this argument is set to false, we should ensure that the file specified in
the FileName argument does not exist. Otherwise an error occurs when our
macro is executed.
3. Unicode: This Boolean argument that dictates the encoding of characters.
If set to True, Unicode encoding is used. The default value is False for
ASCII encoding.

ASCII is the most widely used format. Unicode encoding is used if text
can contain characters from languages other than English. We will ignore
this argument and use the default ASCII encoding since it is the encoding
style widely used for information transfer.

Example:
The code below creates a text file. We assume that the file does not exist.
But, we should ensure this by using the ‘FileExists’ method of
FileSystemObject and then deleting the existing file if required.

Both, ‘OverWrite’ and ‘Unicode’ arguments are ignored and hence their
default values ‘False’.

Notice that it is not necessary for the extension to be “.txt”.
Set ts = fso.CreateTextFile _
(“C:\DBUpload\InputData062015.dat”)


b) OpenTextFile method

This method opens an existing text file and then returns a TextStream object
referring to that file. We can use the new TextStream object to read from, write
to or append to the file. The syntax is
Set ts = fso.OpenTextFile _
(FileName [, iomode[, create[, fmt]]])

Arguments:
1. FileName: This is a string containing the full path of the file that is to be
opened. It can be the path enclosed in quotes (“ ”) or a string variable.

2. iomode: The Input-Output Mode. It is one of the predefined constants that
indicates the operation that the TextStream will perform on the file. It can
take one of the following three values:
Constant Stands For
ForReading Open the file only for reading. We cannot write to the
file.
ForAppending Open the file only for adding content to the end of the
file. Any content already existing in the file is
preserved.
ForWriting Open the file for writing. Any content that existed in the
file earlier is deleted before new content is added.

3. create: This is a Boolean value. If set to True, a new file is created in case
the file specified in ’FileName’ argument does not exist. The default value
is False.
It is better to use the default value and to check the existence of the file with
FileExists method of FileSystemObject object before we try to open the file.

4. fmt: It is one of the predefined constants that indicates the format of the file
to be opened. The default value is TriStateFalse which indicates that the file
is to be opened as an ASCII file.

We will always use the default value. This also means ensuring that we are
opening only ASCII encoded text files.

Constant Stands For


TriStateUseDefault Open the file using the default value set by the
system.
TriStateTrue Open the file as a Unicode encoded file.
TriStateFalse Open the file as an ASCII file.

Example:
The code below opens the text file that we created using the CreateTextFile
method.

Set ts = fso.OpenTextFile( _
"c:\DBUpload\InputData062015.dat", _
ForAppending,TristateFalse)

c) OpenAsTextStream method

This method opens an existing text file and then returns a TextStream object
referring to that file. We can use the new ‘TextStream’ object to read from,
write to or append to the file.

While method (b) is part of the FileSystemObject Object, this method is part
of the File object. It ‘Opens a File As A TextStream’. The syntax is:

f.OpenAsTextStream([Iomode, [fmt]])


Here ’f’ is a File object as described in section 14.1.4. It is obtained by using
the GetFile method of the FileSystemObject object.
Arguments ’Iomode’ and ’fmt’ are the same as those described for method
(b).

14.3.2 Properties and Methods of TextStream


Properties
1. AtEndOfLine: This property takes a Boolean value. A value of True
indicates that the file pointer is just before the end of the current line.
In a text file, each line is ends with a special character called ‘EndOfLine’
marker. If we write an instruction to read a line from the file, our program
will read all characters from the previous end of line marker to the next end of
line marker. Thus reading a full line.

This property is useful when we are reading a file one character at time and
want to stop when we reach the end of the current line.

2. AtEndOfStream: This property takes a Boolean value. A value of True
indicates that the file pointer is at the end of the TextStream, essentially the
end of the File that our program is working on. No further reading is allowed.
If the TextStream is opened for writing, any writing happens from this point
onwards.
This property is most useful in deciding when to stop reading a file because
any attempt to read from a file beyond its end point results in an error. Code
examples will make this clear.

This property is most commonly used as a control for a while loop as shown
in the code here
While Not(ts.AtEndOfStream)
‘//** read another line or character **//
‘//** from the file and process it here **//
Wend

Methods
1. Close: Closes the current TextStream object and the related file. Syntax is as
follows:
‘//** ts is a TextStream object **//
ts.Close

2. Read: This method takes a number as a parameter, reads that many number of
characters and returns a string containing those many characters.

This method is useful if we are reading files having a bulk of text not
organized as rows. It can be used effectively only if we know exactly the
number of characters that we want to read at each attempt.

This method has been included just for information, we will not use this
method.
The syntax is as follows:

Dim strReadChars As String
ts.Read(nChars)

’ts’ is a TextStream object. ’nChars’ is the number of characters we want to
read from the file.

3. ReadAll: Reads all the characters in the TextStream and returns the string.
This method is only useful for small files (a few KB in size). Reading an
entire file at a time consumes a lot of RAM. It is better to use other methods.
The syntax is as follows (’ts’ is a TextStream object):
Dim strReadFile As String
strReadFile= ts.ReadAll

4. ReadLine: This method reads a file ‘one line at a time’ and returns a string
containing the line.

So how does the program know that it has read a line? This depends on the
way the file was written.

For a file to be read line by line, it should have been written line by line.
Whenever a text file is written with breaks between lines, a special character
called ‘NewLine’ character is added at the end of each line.

In our program, when we write an instruction for the TextStream object to
read one line, the object starts reading from the current position of the file
pointer and keeps reading till it reaches a NewLine character. At this point it
stops. The new line character is not considered, and all characters read up to
that point are returned in a string.

This is the most useful method when working with text files from within
Excel because most of the time, the data that needs to be processed in excel is
provided in text files having multiple lines. These files are themselves
organized as rows and columns.

Dim strLine As String
strReadFile= ts.ReadLine

5. Write: This method just takes a string and puts it into the file starting at the
current position of the file pointer (at the end of the file).

This is not a very useful method, because it just dumps the text into the file. If
we want to separate two blocks of text that we write, then we have to ensure
that we write in some separating character like space (“ “) or a (;) in the next
write operation. We can even write a new line character (’vbNewLine’) if we
choose to.

The syntax for the method is:

ts.Write(strData)

’strData’ is the string that has to be written to the file.

6. WriteLine: This is the most useful method when writing information to a text
file. Starting at the current position of the file pointer, it writes the string that
we provide and then adds a NewLine character at the end. Any new content
that is added, automatically begins in a new line inside the file.

The syntax for the method is as follows:

ts.WriteLine(strData)

7. WriteBlankLines: This method simply puts a specified number of NewLine
characters in the file. It is similar to pressing the ‘Enter’ key multiple times
when writing the file manually.

This method is useful if we are trying to create a formatted report in the text
file. For example, we want to add a line showing total value after leaving a
gap of few lines.
ts.WriteBlankLines(nLines)

’nLines’ is an integer value specifying the number of blank lines that we wish
to insert in the file.

Skip: When reading a file, this method skips (reads and then discards instead
of returning) a specified number of lines.This method is used in conjunction
with the Read method. The syntax is as follows (ts is a TextStream object.
nChars is the number of characters we want to read from the file).

ts.Read(nChars)

This method is useful only if we know the exact structure of the file and, how
many characters to read in order to extract the data that we want.

Please note that ‘discard’ does not mean removed from the file. It only means
removed from the memory and hence no longer usable by our program.

8. SkipLine: When reading a file line by line, this method skips the next line
that it encounters. Meaning, from the current position, all characters up to the
next NewLine character are read, and then discarded.

In our programs, we will not assume any knowledge of the location of blank
lines. So we will read all lines and test each one to see if it serves our purpose.
We will discard only those lines that are not useful to us.


14.3.3 Code to read a text file into Excel


Problem:
Often we get tabular data in text files. In each row, the column values are
separated by a specific character like a comma ( , ), semi-colon ( ; ) or a pipe ( | )
and so on. We have to bring in this data into Excel so we can process it further
probably by combining it with other data.
Solution:
We use the TextStream object to read the file, one line at a time. We split a line
into an array containing the column values. The we put the required values on a
worksheet.
In the code that follows we assume that
1. the table in the input text file contains a header row which we want to discard.
2. we know the data contained in each column
3. all the columns have to be placed in the workbook. However, if required we
can discard some of the columns. We can also do some processing on the
column values if required.
The input parameters are as follows:
rngTL: Top left corner of the range in a worksheet where writing of data will
begin.
fFile: The file object that represents the file to be read
Sub ReadNFillRange(ByRef rngTL As Range,ByRef fFile As File)
Dim rw,col,posStart, posEnd As Long
Dim arrStr As Variant
Dim ts As TextStream
Dim inpLine As String
Dim wks As Worksheet
Set ts = fFile.OpenAsTextStream
Set wks = rngTL.Worksheet
‘//** Read the column headers, don’t process them **//
ts.ReadLine
rw = rngTL.Row
col = rngTL.Column
While ((ts.AtEndOfStream <> True))
inpLine = ts.ReadLine
arrTxt = Split(inpLine, "|")
posStart = LBound(arrTxt)
posEnd = UBound(arrTxt)
For i = posStart to posEnd Step 1
‘//** Process the array elements as required. **//
‘//** Discard any data that is not required **//
‘//** before putting the values in the worksheet. **//

wks.Cells(rw,col+i).Value = arrTxt(i)
Next i
rw = rw + 1
Wend
ts.Close
‘//** Release object references by setting them to Nothing **//
End Sub

14.3.4 Code to write Excel data to a text file


The following code takes a range and for each row in the Range, transfers the
data in first, third and fifth column to a text file. Notice how the line of text to be
written is compiled using the concatenation operator (&) and how the columns
are separated using “|” character. For another example, please refer to the topic
“Create the script file from inside VBA” in section 12.3.

Sub Write2TextFile(ByRef rng As Range)

‘//** Declare the FileSystemObject(fs), **//
‘//** Declare the TextStream(ts) and String(strContent) **//

Set ts = fs.CreateTextFile(“C:\newTextFile.txt")

For r = 1 to rng.Rows.Count Step 1

‘//** Compile the string to be written. **//
strContent = rng.Cells(r,1) & “|” & _
rng.Cells(r,3) & “|” & rng.Cells(r,5)

ts.WriteLine strContent
Next r

ts.Close
End Sub

Chapter 15
Working with MS Word
In this chapter we will discuss how we can automate the transfer data from Excel
to MS Word. This chapter is about transferring data using macros but first let’s
look at some simpler methods available in MS Word.
15.1 Easy and straightforward – Linking
This is the most straightforward way of transferring data between Excel and
Word.
1. In Excel, just select the range or chart that has to be transferred.
2. In the Word Document, place the cursor where the data has to be inserted.
3. Right click and in the popup menu select one of the paste link options as
shown in figure 15.1a. Both options will paste a table that is linked to the
original Excel file.

Fig 15.1a Using Object Linking to bring Excel data into Word

Alternatively, we can use the key combination “Alt + e + s + u” then make a


selection for the format of the pasted item from the available options listed on
the Paste Special dialog box. Figure 15.1b shows the dialog box for pasting an
Excel range in Word. Ensure that the “Paste Link” radio button is selected on the
left hand side.

Fig 15.1b Paste Special dialog box for pasting an Excel range.




A similar box appears when pasting charts.
1. For a data range.
If inserted as
A) “Formatted text” or “HTML Format” data is pasted as a table and items in
individual cells can be selected.
B) If “Worksheet object” or Bitmap option is selected, the data pasted as an
image. Text in cells cannot be selected.
It is better to paste it as text because, once we send out the Word report, some
team member may have to copy specific data points into another Excel or PPT
file. If we provide the data as an image, they would have to type the details by
hand.
2. For a chart
We can select either “Excel Chart object” or “Bitmap” option.

Advantage of Linking:
The good thing about linking is that when data in the chart or range changes, all
we have to do is right click on the pasted matter and select update link.
Disadvantages of Linking
a. If we send the Word document to someone else (or even move the Word file
to another computer), the linked workbook is not included. When the
recipient opens the word file a nasty error shows up shown in the following
figure.

If we click the “Update” button, we would get another error shown in


following figure

Now if we click on the “Edit Links…” we would get the “Edit Links” dialog
box. Look at the “Status” column, it shows and error. This means that the
linked data cannot be updated.

So before we send the document we need to make a copy of the document,


break the links and send the copy instead of the original document.
To break links:
1. In the “Data” ribbon, click the “Edit Links” button. This will bring up the
“Edit Links” dialog box.

2. Select the links one by one and click on “Break Link” button for each link

b. The method works only for tabular range of data present on a worksheet. If
data is in non-adjacent cells we would have to create and update many
individual linked cells.

c. Data has to be on a worksheet. We cannot add data calculated inside a macro
into a word document using this method.
15.2 An alternative - Mail Merge
Mail merge is a convenient option built into MS word that allows bringing data
into word not just from Excel, but from a variety of sources.
We will not delve into details of implementing mail merge because the topic is
well covered in a wide variety of literature on MS Word. Instead we will look at
the pros and cons so that we can make a good decision while choosing the
method.
Pros:
1. Easy to implement compared to macros.
2. Useful to create multiple copies of same document each of which has a small
variable portion
Cons:
1. Dependent on data being organized as rows and columns.
2. Data has to be on a worksheet. We cannot add data calculated inside a macro
into a word document using this method.
3. Cannot be used to transfer graphics.
4. Cannot be used to transfer an entire rectangular range like a table of data.
15.3 Bit difficult but flexible – Macros
In the following sections, we will look at an option that would require some
initial effort but once set, the subsequent efforts are at a minimum.
This method uses macros and allows us to transfer a large amount of data very
quickly to specific locations inside the word document.

15.3.1 Connecting Word to Excel


In the Visual Basic Editor, go to “Tools” in the main menu and select
“References”. In the References dialog box (figure 15.3.1) that comes up scroll
and locate “Microsoft Word X.X object Library”. The numbers “X” will depend
on the version available on your computer.

Fig 15.3.1a References dialog

Select this option and click on “OK”


That’s it. Now we can go ahead and use the objects of Word in our macro.
15.4 The MS Word Object Model
Let us look at the most important objects that are useful when transferring data
from Excel to Word.

The diagram here shows the most important objects arranged in a hierarchy.
Ovals represent collections of objects of a particular type, rectangles represent
individual objects.
Any object is accessed via the object above it. Connections between objects
don’t refer to the actual design of the objects, but to the path to be taken to
access a particular object.
Hence, to get a reference to a particular Range, we must first get a reference to a
Word Application object, then to the collection of all open Documents. From
there we get a reference to a particular Document that contains the range. We can
then access the range directly (difficult) or get a reference to a bookmark that
contains the range and then access the range (relatively easy if the bookmark is
exists).
15.4.1 Application
This is the topmost object in the Word object hierarchy. It gets created when a
user launches Word from the Start menu or another program launches Word.
Usage:
To get access to properties and methods that can be used with all open Word
Documents.
To start automating Word, we need to declare an application object in our macro.
Here is the syntax for declaration.
Dim wdApp As Word.Application

15.4.2 Using a running Word application – GetObject


Function
Notice that when declaring a variable for the Application object, we have not
used the ‘New’ keyword.
This is because the user may already be working on another Word document or
on the same report that our macro will update. If we use the ‘New’ keyword, MS
Word is launched all over again as a separate application. This is something we
should always avoid.
To get around this problem we will use the ‘GetObject’ function of Visual Basic.
This is a fundamental function that is always available for use within macros. It
is used to get a reference to an object that is already being used in a running
program.
We will not delve into the details of the function but review only the essential
aspectss that are required for our purpose which is to get reference to the Word
application object which is already running. Here is the syntax for the function
GetObject(strObjFilePath, strObjClassName)

The function takes two optional string arguments


a. strObjFilePath: This is the path of the file that will contain the object we are
seeking. This is not useful for our purposes and we will omit it.
b. strObjClassName: This is a string holding the name of the object we are
seeking. In our case the value of this argument will be “Word.Application”
If the Word application is already running, GetObject function will hand us a
reference to the application object. However, the GetObject function will
generate an error if the application is not running. We will check for this error.
If the error occurs, we will launch the application using the ‘New’ keyword.
To handle all these processes, we will create a separate function ‘GetWordApp’.
Notice that the function returns an entire application object. The code for this
function is as follows.
Function GetWordApp() As Word.Application
Dim wdapp As Word.Application
On Error Resume Next
Set wdapp = GetObject(, "Word.Application")
If wdapp Is Nothing Then
Set wdapp = New Word.Application
End If
Set GetWordApp = wdapp
End Function


Now in the sub-procedure of our macro that opens and modifies our Word
document we can use the following lines of code:
Dim wdApp As Word.Application
Set wdApp = GetWordApp()

15.4.3 Application properties and methods


Properties
1. Documents: Returns a collection of all documents currently open in MS
Word. We will see the details of this object in the next section. The syntax for
accessing this object is:
wdApp.Documents

2. Visible: A Boolean value indicating if the Word application is visible on the


screen.
wdApp.Visible = True
Methods
1. Quit: This method closes the running Word application. Before we call this
method, we should ensure that all documents are saved.
wdApp.Quit
15.4.4 Documents Collection
This is a collection of all the word documents that are currently open. A specific
open document can be accessed using the name of the document. Here is the
syntax:
Dim wdDoc As Word.Document
Set wdDoc = wdApp.Documents(“SalesReport.docx”)

In the code, wdDoc is a Word Document object (described next) that is a


reference to a specific open document. Let us look at some important methods of
this collection.
Methods
1. Add: This method creates a new blank word document and returns the
Document object. The syntax is as follows:
Set wdDoc = wdApp.Documents.Add

2. Open: This method opens an existing document and returns a Document


object. This method takes 1 required arguments and over 10 other optional
arguments.

The complete set of arguments is not important for automating Word. Most of
them deal with password protected files, corrupt documents and templates. We
will use only two arguments that should allow us to handle most of the
situations. Here is the syntax for calling this method

If we are expecting our document to be password protected, we should open it
manually first and then run our macro.
Set wdDoc = _
wdApp.Documents.Open(strFilePath, , , , , , bRevert)


** Notice the six commas between the two arguments.
strFilePath: This is a string holding the path of the file that we want to open.
This is the only required argument.
bRevert: This is a Boolean value that indicates what to do if our user has
already opened the document. Here are the two values for this argument and
their effects
a) True: Unsaved changes will be discarded and document will be reopened
and reference will be provided to our macro.

b) False: Reference to the currently opened document will be provided to our
macro.

In our case it is possible that the user may be working on the document before
running our macro. Hence we should always set ‘Revert’ to False.

If we don’t want to use a lot off commas we can use names of the arguments
in the method call. Here is the alternate syntax
Set wdDoc = wdApp.Documents.Open _
FileName:="C:\Reports\Sales.doc", Revert:=False

15.4.5 Document
This object represents the word document that our macro will modify. Let’s look
at some important properties and methods. For the code in this section, consider
a Document object declared as wdDoc which has been initialized using the Open
method.
Properties
1. Bookmarks: Returns a collection of all bookmarks in the particular
Document. We will see the details of this object in the next section. The
syntax for accessing this object is:
wdDoc.Bookmarks
Methods
1. Close: This method closes the Word document. It takes three optional
arguments. The first is a Boolean value and deals with saving the changes.
The remaining two deal with format and routing. Here is the syntax that will
save the changes and closes the document

wdDoc.Close(True)

2. Range: This method returns a Range object that represents an area in the
document. The syntax for this method is:
Set wdRng = wdDoc.Range(start,end)

The arguments ’start’ and ’end’ are Variant values denoting the start and end
character positions of the Range. The first character in the document is at
position ‘0’.
In the code shown, wdRng is a Range object that we will see in detail in an
upcoming section. We will also ensure that we don’t have to know the exact
values of start and end arguments.
3. Save: This method saves ALL the open word documents in the Documents
collection. If a document has not been saved previously, a dialog box appears
asking the user to enter a name for the document.
wdDoc.Save

15.4.6 Range
A Range represents a specific area in a document. A range can be obtained by
specifying the starting and ending character positions in the document.
The advantage of using a range is that once we get a reference to a range in our
macro, we can place a variety of contents inside the range.
A range can contain text, images or tables. Let’s look at some important
properties and methods of this object.
For the code in this section consider a Range object declared as wdRng which
has been initialized using the Range method of the related Document.
Properties
1. Start: Holds a Long Integer value representing the starting character of the
range. Syntax for accessing/setting this property is:
wdRng.Start = 100

2. End: Holds a Long Integer value representing the last character of the range.
Syntax for accessing/setting this property is:
wdRng.End = 500

We will try to work without knowing the exact values of Start/End properties.
It is better to refer to the values of an existing range.
This concept will become clear when we write the code for data transfer.

3. Text: This property holds the text (without any formatting) that is contained
inside the range. Following line of code sets the text in a range.
wdRng.Text = “Hello world”
4. Tables: This property returns a collection of all the tables contained in a
range. A specific table can be accessed using its index number. The following
example shows how we can retrieve the first table in the range:
wdRng.Tables(1)

Although it is possible to have more than one table inside a range, it is better
to have only one table in each Range. This will avoid accidental deletion or
change in other tables when one table and its range are being modified.
Tables are themselves very complex objects with their own methods and
properties. Since our tables will be brought in from Excel we will not delve
into detail of the Table object.
However, there is one method of the Tables collection that we will require.
This method is used to delete a table contained inside a range. The following
line of code deletes the first table contained within a range:
wdRng.Tables(1).Delete

Methods
1. Delete: this method is used to delete the range. Although the method takes
arguments, all of them are optional and are not required when transferring data
from Excel to Word.
The syntax for this method is:

wdRng.Delete

2. PasteExcelTable: This method will paste a previously copied Excel Range.
The formatting applied will depend on the value of one of the arguments. The
syntax for this method is as follows:
wdRng.PasteExcelTable _
(bLinkToExcel, bWordFormatting, bRTF)

All three arguments are Boolean and are required to be specified. Here is a
description:
a. bLinkToExcel: A value of True will preserve a link to the Excel file.
Changes made in Excel will get reflected in the Word document when the
link is updated.
Setting this argument to False will paste a static table.

b. bWordFormatting: Setting this argument to True format the table based on
the formatting currently applied to Word document. Setting it to False will
preserve the original Excel formatting.

c. bRTF: Setting this argument to True will paste the table in RTF format.
Setting it to false will paste table in HTML format
We don’t want our tables to be linked to our Excel file, we want to preserve
the Excel color and font formats, so we will set the first two arguments to
False.
Pasting as RTF slightly distorts the text in the table, so we will paste the table
in HTML format. Hence we will set the third argument to False.
Hence in our example code we will use the following syntax:
wdRng.PasteExcelTable(False, False, False)

3. PasteSpecial: This method allows us to transfer contents copied from other


programs into word in several different formats. The main use of this method
is to transfer charts and images from Excel.
The syntax for the method is as follows:
wdRng.PasteSpecial(IconIndex, Link, Placement, _
DisplayAsIcon, DataType, IconFileName, IconLabel)

About the arguments:
a. Link: This is a Boolean argument and specifies if we should maintain a link
to the original file. Since we are not concerned with linking we will set this
to ‘False’

b. Placement: This argument takes one of the two values.
1. wdFloatOverText: will cause the pasted item to be placed above the text
in the document. It will hide the text behind it.

2. wdInLine: will cause the pasted item to be placed such that the text in the
document appears around the pasted item. This is the default value and
we will use it.

c. DataType: This is the most important argument. It specifies how the pasted
item should be inserted in the word document. The values allowed are as
follows:

Name Value Description

wdPasteBitmap 4 Bitmap.

Device-independent
wdPasteDeviceIndependentBitmap 5
bitmap.

wdPasteEnhancedMetafile 9 Enhanced metafile.

wdPasteHTML 10 HTML.

wdPasteHyperlink 7 Hyperlink.

wdPasteMetafilePicture 3 Metafile picture.

wdPasteOLEObject 0 OLE object.

wdPasteRTF 1 Rich Text Format (RTF).

wdPasteShape 8 Shape.

wdPasteText 2 Plain text



We will use ‘wdPasteBitmap’ most frequently to insert charts. We will use
other methods to insert text and tables.
d. IconIndex, IconFileName, IconLabel, DisplayAsIcon: These are optional
arguments and are useful only if we want to display the pasted item as an
Icon. We are interested in pasting images and tables, so we will ignore
them.

15.4.7 Bookmarks collection


A Bookmark represents a named area in a document. We can think of it as a
Range with a name.
A Word document can contain upto 2 Billion bookmarks, so practically we can
have as many bookmarks as we want.
Bookmarks can be created either within a macro or manually. We will see the
details of the Bookmark object and how to create them in the coming sections. In
this section we will see the collection of all bookmarks created in a document.
Please note that Word adds its own system generated Bookmarks at various
locations. But this should not hinder our work.
All Bookmarks created in a Word document are held in the Bookmarks
collection of that document. This collection is accessed using the ‘Bookmarks’
property of the document.
A specific Bookmark can be accessed using the following syntax:
Dim wdBkMrk as Word.Bookmark
Set wdBkMrk = wdDoc.Bookmarks(“Bookmark1”)

Note that trying to access a bookmark that does not exist will cause an error and
our macro will stop executing. It is better to check if the bookmark exists before
trying to work with it. We can do the check using the Exists method listed here.
Let us see some important methods of this collection.
Methods
1. Add: This method adds a new bookmark in the document and then returns the
new bookmark. The syntax is as follows
Set wdBkMrk = _
wdDoc.Bookmarks.Add(strName, wdRng)

About the arguments:


a. strName: This is the name of the bookmark with which we can refer to it.

b. wdRng: This is the Range object that we want to refer to with our new
Bookmark.

2. Exists: This method allows us to check if a Bookmark with a certain name
exists in our document. It returns True if the bookmark exists else it returns
False. The following code displays a message based on whether a bookmark
exists
If wdDoc.Bookmarks.Exists(“Bookmark1”) Then

MsgBox (“Bookmark1 found”)
Else
MsgBox (“Bookmark1 not found”)
End If

15.4.8 Bookmark
As noted earlier, a Bookmark represents a named area in a document. We can
think of it as a Range with a name.
The advantage is that we can access the bookmark and its corresponding Range
in our macro using the bookmark’s name. Once we get the Range, we can
modify the contents in the Range.
Properties
1. Range: This method returns the range that a particular bookmark refers to.
Methods
1. Delete: This method deletes a specific bookmark and its associated range.
15.5 Creating a Bookmark manually
To create Bookmarks for our content, we have to insert the content manually the
first time around. Once the content is placed, we proceed as follows:
Step1: Select the content
a. Selecting a chart or image
A chart, since it is pasted as an image, or an image itself, can be selected
simply by clicking on it. Small boxes appear around the image. Four at the
corners and another four each in the middle of each of the four sides.

b. Selecting a table
A table can be selected by first clicking anywhere inside the table so that a
crosshair appears at the top left corner of the table. Now we click on the cross
hair to select the full table.

c. Selecting text
Text can be selected in the same way as when we select for copying or cutting
the text. It is better to select one extra blank space at the beginning and the
end of the required piece of text.

Step 2:
In the “Insert” ribbon, click on “Bookmark” option in the “Links” group
This brings up the “Bookmark” dialog

Bookmark option in the ‘Insert’ Ribbon



Step 3:
· Enter the desired name of the bookmark in the box labelled “Bookmark
name”

· Click on “Add” button.

The new bookmark will appear in the list and will be now available for use in
our macros.
‘Bookmark’ dialog

If we want to remove a particular bookmark.


· Bring up the Bookmark dialog as described in Step 2.
· Select the Bookmark from the list.
Click on “Delete” button.

15.6 Creating the base file
For the very first time, we have to create the report manually. Subsequent
updates will be done using an Excel macro.
We will use the term ‘Base File’ to denote the file that our macro will update. We
avoid using the term ‘Template’ since it has a different meaning in context of MS
Office applications.
Advantages of our approach:
1. Even if some content of the Word document is edited by someone else, as
long as the bookmarks are in place, our macro can still update it.

2. The document for the Base File does not have to be a blank document. It can
be a file that is in use or even an organizational template.
Here are the steps:
a. Create all tables and charts in Excel. Format them as required. (Including
colors and fonts)
b. Paste all tables, charts and summary text into the Word file manually.
Position them as required.
c. Next select each chart, table and lines of text (ones that our macro will
update) and create a bookmark.
15.7 Updating the Base File using macros
Once we have created the base file with all the bookmarks, we can create macros
to update the data held in the bookmarks. In the coming sections we will see the
code for updating different types of data like a piece of text, a chart, a table and
so on.
For eah type of data transfer, only the essential lines of code have been shown in
order to conserve space. In actual usage, the code lines should be part of a sub or
function procedure.

15.7.1 Transfer text to a specific location


Consider the pieces of text shown in the figure 15.7.1a
The highlighted/underlined parts show the text that will be updated by our
macro.

1. The total volume of sales in the quarter was 50000 Fig. 15.7.1a Text that will
be updated by our macro
units
2. This shows a 10% increase Q-o-Q
3. The revenue for the quarter was $1.25 million


We need to create the following bookmarks (create one for each highlighted
piece of text):
Bookmark Holding value of Current value in figure
Name 15.7.1a
SalesVol Quarterly sales volume 50000
QoQIncrease Quarterly increment 10%
QtrRevenue Quarterly revenue $1.25 million

Let us look at the code for changing the text in these bookmarks. Note that in the
code we have set the text in 3 different ways. Directly typing the text in the code,
using a calculated value and using a value from a cell.

The variables used in the code are as follows:
nQoQ Variable (Integer) that is calculated in our macro.
dRev variable (Double) that takes its value from a cell on a
worksheet.
wdrng Word.Range object in the Word Document.
doc Word.Document set using the Open method

Here is the code:
Dim nQoQ as Integer
Dim dRev as Double
Dim wdrng as Word.Range
Set wdrng = doc.Bookmarks("SalesVol").Range
‘//** Putting text directly in the range **/
wdrng.Text = "50000"
‘//** Replacing the text of the range deletes the bookmark **//
‘//** so recreate the bookmark **//
‘//** To be cautious, just check for the bookmark, **//
‘//** if it doesn’t exist, recreate it **//
If Not doc.Bookmarks.Exists("SalesVol") Then
doc.Bookmarks.Add "SalesVol", wdrng
End If
‘//** Release the reference to the first range, **//
‘//** so that the variable can be used for another range **//
Set wdrng = Nothing
Set wdrng = doc.Bookmarks("QoQIncrease").Range
‘//** Setting the text using a calculated variable **//
wdrng.Text = nQoQ & “%”
‘//** Recreate the bookmark **//
If Not doc.Bookmarks.Exists("QoQIncrease") Then
doc.Bookmarks.Add "QoQIncrease", wdrng
End If
Set wdrng = Nothing
Set wdrng = doc.Bookmarks("QtrRevenue").Range
‘//** Setting the text from a value in a cell **//
fRev = ThisWorkbook.Worksheets(“Sales”).Cells(5,5).Value
wdrng.Text = “$” & fRev &" million"
If Not doc.Bookmarks.Exists("QtrRevenue") Then
doc.Bookmarks.Add "QtrRevenue", wdrng
End If

Notice that we have directly formed strings using numeric variables (’nQoQ’
and ’fRev’). If we require a special formatting (like specific decimal places,
thousand separators and so on), we whould use the ‘Format’ function described
in section 8.4.

15.7.2 Transfer tabular data


Consider that our Excel workbook has a worksheet named “SalesSheet”. This
worksheet contains a summary table starting at cell A1.
In our Word document, we have pasted this table once manually, and created a
bookmark for it named “Summary_Table”. Next time, to update the table, we
will run our macro which will delete the old table from the Word document and
replace it with the updated table from Excel.
The Excel range for the table can be created using any of the methods described
in the earlier sections of this book. For the code in this section we assume that
the table starts at cell A1 and has a header row with no blank cells. Hence we
can use the “CurrentRegion” property.
The variables used in the code are as follows:
wdBookStart Long Integer variable used for holding the starting position of
the Range (in Word) that contains the table.
wdBookEnd Long Integer variable used for holding the ending position of
the Range (in Word) that contains the table.
wkstemp Worksheet object referring to the sheet that holds the data to be
pasted to Word document.
wdrng Word.Range object in the Word Document.
doc Word.Document set using the Open method
rngTbl Excel Range object that will refer to our data in Excel.

Here is the code.
Set wdrng = doc.Bookmarks("Product_Table").Range
wdBookStart = wdrng.Start
wdBookEnd= wdrng.End
wdrng.Tables(1).Delete
Set wdrng = Nothing
If doc.Bookmarks.Exists("Product_Table") Then
doc.Bookmarks("Product_Table").Delete
End If
Set wdrng = doc.Range(wdBookStart, wdBookStart+1)
Set rngTbl = wkstemp.Range("A1").CurrentRegion
rngTbl.Copy
wdrng.PasteExcelTable False, False, False
Application.CutCopyMode = False
wdBookStart = wdrng.Tables(1).Range.Start
wdBookEnd = wdrng.Tables(1).Range.End
Set wdrng = Nothing
Set wdrng = doc.Range(wdBookStart, wdBookEnd)
doc.Bookmarks.Add "CompTable", wdrng

15.7.3 Transfer a chart


In this section we will see the code for transferring a chart into a Word
document.
Consider that our Excel workbook has a worksheet named “SalesSheet”. This
worksheet contains a chart that is named “Top5Components”
In our Word document, we have pasted this chart once manually, and created a
bookmark for it named “Top5Components”. The name doesn’t have to be same
as the Excel chart.
Notice how in the code we have used a string variable to hold the name of the
bookmark in Word and the chart in Excel. The variables used in the code are as
follows:

wdBookStart Long Integer variable holds the starting position of the
Range (in Word) that contains the chart
strBkMrkName String variable that holds the name of the bookmark and the
chart.
wdrng Word.Range object in the Word Document.
doc Word.Document set using the Open method
chobj Excel ChartObject object that will refer to our Excel chart.

Here is the code.
Dim wdBookStart as Long
Dim strBkMrkName as String
Dim chobj As ChartObject
strBkMrkName = “Top5Components”
‘//** Retrieve the Range that the bookmark refers to **//
Set wdrng = doc.Bookmarks(strBkMrkName).Range
‘//** Note the start position of the Range, **//
‘//** will be used to recreate the bookmark later **//
wdBookStart = wdrng.Start
‘//** Delete the range and hence its contents. **//
wdrng.Delete
Set wdrng = Nothing
If doc.Bookmarks.Exists(strBkMrkName) Then
doc.Bookmarks(strBkMrkName).Delete
End If
‘//** Create a new Range, **//
‘//** Starting at the same position as the earlier range **//
‘//** Just one character wide **//
Set wdrng = doc.Range(wdBookStart, wdBookStart + 1)
Set chobj = _
Worksheets("SalesSheet").ChartObjects(strBkMrkName)
‘//** Copy the chart from the Excel worksheet **//
chobj.CopyPicture xlScreen, xlBitmap
‘//** Paste the chart as an image in the Word document **//
wdrng.PasteSpecial False, False, , False, wdPasteBitmap
‘//** Recreate the bookmark using the new Range but the same name.**//
doc.Bookmarks.Add strBkMrkName, wdrng
Application.CutCopyMode = False
Set wdrng = Nothing
Set chobj = Nothing


15.8 Transfer a table with varying number of rows
Consider the following problem
Problem:
In the report that we create in MS Word, we have to paste a table. The number of
rows in this table keeps varying every time we create the report.
Fortunately, in Word the table and the related range will automatically expand
over multiple pages. But this also means that the remaining pages will not have
the column headings.
Solution:
Here is one possible solution.
In the Base File, ensure that this table starts on a new page, and that it is the first
item after the header.
In the Base File, paste enough number of rows in the table to occupy a single
page. Now create a bookmark for this table.
Note down the number of rows that would fit in the table. Suppose the number is
‘n’
In Excel, create a temporary sheet. Paste a copy of the table from its original
location into the temporary sheet along with its header row.
Now in the temporary sheet, after every nth row, insert a row and copy the
header row of the table in this new row.
Now we have the main table divided into a number of separate tables each
having ‘n’ rows.
Transfer this new table to the Word document using the method described
earlier.
When the table flows over to the next page, the first row will have column
headings.
Now we will look at the code for this solution. In the temporary sheet, we will
start the table at the first row and from column A. the sub-procedure
“CreateMultipageTable” will create the table on the temporary sheet.

The variables used in the code are as follows:
wksMain Excel worksheet (main worksheet) that contains the main
table.
colTableFirst Integer. It is the first column of the table on the main
worksheet
rwTableFirst Long Integer. It is the first row of the table on the main
worksheet
rwLast Long Integer. It is the last row of the table on the main
worksheet
nSet Long Integer. Denotes the number of rows of the table that
will fit on one page of the Word document.
wksTemp Excel worksheet. Contains the new table split into multiple
tables each witn ‘nSet’ rows.
nCut Long Integer. Denotes the row number of the row after
which the next copy of header row will be inserted in each
iteration.

Note: This method will transfer the entire data to Word. However, the
transferred table will look good only if the very first row of the table will start
at the very first line on a particular page.

This is one of those macros where reading the code alone will not help. The
reader is requested to actually create a long table in Excel and try transferring it
to Word


Here is the code for creating the table spread across multiple pages.
Sub CreateMultipageTable()
Dim nCut, nset As Long
Dim rwTableFirst, rwLast As Long
Dim colTableFirst, ncols As Integer
Dim wksTemp, wksMain As Worksheet
Dim bKeepLooping As Boolean
Set wksTemp = ThisWorkbook.Worksheets("Sheet38")
Set wksMain = ThisWorkbook.Worksheets("Sheet1")
rwLast = wksTemp.Cells(wksMain.Rows.Count,rwTableFirst). _
End(xlUp).Row
wksMain.Cells(rwTableFirst, colTableFirst). _
CurrentRegion.Copy(wksTemp.Cells(1, 1))
nset = 10
nCut = nset
bKeepLooping = True
While bKeepLooping
wksTemp.Rows(nCut + 1).Insert
wksMain.Cells(rwTableFirst, colTableFirst). _
Resize(1, nCols).Copy(wksTemp.Cells(nCut + 1, 1))

rwLast = rwLast + 1
nCut = nCut + nset
If nCut >= rwLast Then
bKeepLooping = False
End If
Wend
End Sub





Chapter 16
Working with MS PowerPoint
In this chapter we will discuss how we can transfer data automatically from
Excel to MS PowerPoint. We will look at the following types of data transfer
operations:
1) Transfer a piece of text to a specific slide
2) Transfer a fixed range (tabular data) from Excel to a particular silde
3) Transfer a chart from Excel and
4) Transfer a range (and hence the table of data) that would have variable
number of rows. This table may have to be spread across multiple slides.
Let’s get started righ away.
16.1 Connecting PowerPoint to Excel
In the Visual Basic Editor, go to “Tools” in the main menu and select
“References”. In the dialog box that comes up scroll and locate “Microsoft
PowerPoint X.X object Library”. The numbers “X” will depend on the version
available on our computer.
Select this option and click on “OK”
16.2 The MS PowerPoint Object Model
Let us look at the most important objects that are useful when transferring data
from Excel to PowerPoint.
The diagram here shows the most important objects arranged in a hierarchy.
Ovals represent collections of objects of a particular type, rectangles represent
individual objects.
Any object is contained in the object above it. Containment does not refer to the
actual design of the objects, but to the path to be taken to access a particular
object.
So, to get a reference to a particular Shape, we must first get a reference to a
Powerpoint Application object, then to the collection of all open Presentations.
From there we get a reference to a particular Presentation that contains the
Shape.
Within the Presentation, we first get a reference to the collection of all the Slides
in the file, then to the particular Slide that contains the Shape. From the Slide we
get a reference to the collection of all shapes on that particular slide. Then from
the collection we access the particular Shape we are looking for. Once we get the
shape, we can update its content (a table, chart or text)

16.2.1 Application
This is the topmost object in the PowerPoint object hierarchy. It gets created
when a user launches PowerPoint from the Start menu or another program
launches PowerPoint.
PowerPoint is a single instance application. So even if a user has already
launched the program, we can use the ‘New’ keyword in the declaration and get
a reference to the running application.
The syntax for obtaining a reference to the program is as follows:
Dim pptApp As New PowerPoint.Application

Properties
1. Presentations: This gives us a collection of all the Presentation (ppt, pptx
etc.) files that are currently open. The syntax for accessing this object is:
pptApp.Presentations

2. Visible: A Boolean value indicating if the application is visible on the screen.


pptApp.Visible = True

Methods
1. Quit: This method closes the PowerPoint application. We should ensure that
we have saved all the open presentation files before we call this method.
pptApp.Quit

16.2.2 Presentations Collection


This is a collection of all the Presentation (ppt, pptx etc.) files that are currently
open. A specific open presentation can be accessed using the name of the
presentation file. Here is the syntax:
Dim ppt As PowerPoint.Presentation
Set ppt = pptApp.Presentations(“SalesReport.pptx”)

In the code, ppt is a Presentation object (described next) that is a reference to a


specific open presentation. Let us look at some important methods of this
collection.
Methods
1. Add: This method creates a new blank presentation and returns the
Presentation object that represents the new PowerPoint file. The syntax is as
follows:
Set ppt = pptApp.Presentations.Add(WithWindow)

The argument WithWindow is an optional argument that controls whether the


new file is visible on the screen. It can take the following values
1. msoTrue: The new file is visible on the screen. This is the default.
2. msoFalse: The new file is not visible on the screen.

2. Open: This method opens an existing presentation file and returns a
Presentation object. Here is the syntax for this method:
Presentations.Open(FileName,ReadOnly,Untitled,WithWindow)

The ’FileName’ argument is a string holding the path of the presentation file
to be opened. It is the only required argument for this method, all others are
optional.

The ’WithWindow’ argument has the same meaning as defined for the Add
method. We will use the default value and not specify value for this argument.

The ’ReadOnly’ argument, if set to msoFalse, does not allow us to edit the
file. Since we are concerned with updating the data in the presentation, we
will leave this argument at the default value of msoTrue.

The ’Untitled’ argument, if set to msoTrue, will display the name of the file in
the title window. This is not much relevant at the time of editing the
presentation. We will leave this with its default value.

The syntax that we will most commonly use is:
Dim ppt As PowerPoint.Presentation
Set ppt = pptApp.Presentations.Open(strFilePath)

’strFilePath’ is a string variable that hold the path of the file to be opened.

16.2.3 Presentation
This object represents a specific open presentation file. We will now see a list of
important properties and methods of this object.
Properties
1. Slides: This property gives us a collection of all slides in the presentation.
We will see this collection in more detail in an upcoming section.

2. ReadOnly: This property returns a Boolean value. It returns True if the file is
opened as ReadOnly.
Methods
1. Save: This method saves the changes made to the presentation. The syntax is
ppt.Save

2. Close: This method closes the presentation. Note that this method will not
prompt if the user wants to save the changes. So before calling this method
we should always use the ‘Save’ method to save our changes.
ppt.Close

16.2.4 Slides collection


This collection object represents all the slides in a presentation file. A specific
slide can be accessed as follows:
Dim sld As PowerPoint.Slide

a. By its name:
Set sld = ppt.Slides(“SalesSlide”)

b. By its index:
Set sld = ppt.Slides(1)

Both, Name and Index are properties of the Slide object that we will see in an
upcoming section.
Properties
1. Count: This property gives a Long integer value showing the total number of
slides in the presentation file. The following line of code displays the total
number of slides
MsgBox ppt.Slides.Count

Methods
1. AddSlide: This method adds a new slide to the presentation file and returns a
slide object referring to the new slide. The syntax for this method is as
follows:
ppt.Slides.AddSlide(index, layout)

’index’ is the position at which the new slide has to be added


’layout’ is a layout object specifying any customized layout to be aapplied to
the slide.
It is not advisable to add the layout using this method. If we want a slide with
a customized layout, we can manually create a blank slide with that layout
and then just copy the slide where required to other places in our presentation
using VBA.
2. Range: This method returns a SlideRange object. SlideRange is a group of
specific slides on which we want to perform some common operation.

We will see the details of SlideRange object a bit later. For now, we will just
see the syntax for creating the Range.
Dim srMySlides As PowerPoint.SlideRange
Set srMySlides = ppt.Slides.Range(arrSldNames)

Or
Set srMySlides = ppt.Slides.Range(arrSldIndices)

arrSldNames: is an array of strings. The strings are names of the slides that
we want to group in the SlideRange

arrSldIndices: is an array of Long integers that are the indices of the slides
that we want to group in the SlideRange.

The advantage of this method is that we can include slides that are not in a
sequence, but which we want to treat as a group.

Note that there can be situations where we will have to produce a SlideRange
having just one slide.

3. Paste: If a slide, or group of slides, has been copied previously (see
section 16.2.5 on Slide object), this method will paste the slides in the
current presentation at the specified location.

The pasted slides are returned as a SlideRange object. Here is the syntax:
Set srMySlides = ppt.Slides.Paste(index)

index is a Long integer. It is the position of the slide before which the slides
will be pasted. For example, the following line of code will paste slides
before the slide that is currently at number 3 in the presentation.
ppt.Slides.Paste(3)

If we don’t specify a value for ‘index’, the slides are pasted after the last slide
in the presentation.

16.2.5 Slide
This object represents one specific slide in the presentation file. We have to
retrieve the slide from the Slides presentation. We repeat the syntax here:
Dim sld As PowerPoint.Slide

a. Accessing using slide name:



Set sld = ppt.Slides(“SalesSlide”)

b. Accessing using slide index:

Set sld = ppt.Slides(1)

Both, Name and Index, are properties of the Slide object that we will see now.
Properties
1. Name: This property holds a string containing the name of a specific slide.
any slide that our macro updates in a presentation should have a unique and
meaningful name. This will allow us to refer to them as variables.
The following line of code sets the name of he fifth slide
ppt.Slides(5).Name ="sldSalesSummary"

2. SlideIndex: This property provides a long integer that marks the position of
the slide in the presentation file. The first slide has index 1.
Following line of code retrieves and stores the index of a slide object
Dim idx as Long

idx = SLD.SlideIndex

We cannot set the value of the index. If we paste another slide between any
two slides, the index values will change.
3. Shapes: this property gives us a collection of all shapes placed on a slide. A
shape is any item that is placed on a slide. It may be an image or a table or a
text box.
A specific shape can be accessed either using an index or the shape’s name.
The following line of code retrieves a shape from a slide
Dim shp as PowerPoint.Shape

set shp=sld.Shapes("chtSalesTrend")
Methods
1. Delete: This method deletes a specific slide. Syntax is:
sld.Delete
2. Copy: This method copies a particular slide into the clipboard. Then, this
slide can be pasted in the same or another presentation. The syntax is:
sld.Copy
3. MoveTo: This method moves a slide to a particular location within the same
presentation. The syntax is:
sld.MoveTo(pos)
pos is the new index position where we want the slide to be moved. Other
slides are automatically given new index values.
4. Duplicate: This method will create a copy of a particular slide and place it
immediately after the original slide. The new slide is returned as a Slide
object.
Dim sldDupe As PowerPoint.Slide
Set sldDupe = sld.Duplicate

16.2.6 SlideRange
This object represents a group of specific slides within a presentation. Unlike the
Slides collection it is not a collection of all the slides inside a presentation file.
We can choose which slides we want to add to the SlideRange.
Following lines of code create a SlideRange with three slides. First using slide
indices and then using slide names.
Dim srSlideGroup As SlideRange
Set srSlideGroup = ppt.Slides.Range(array(1,3,5))
Dim arrSldNames as Variant
arrSldNames = Array("sldSales", "sldRevenues", " sldInventory")
Set srSlideGroup = ppt.Slides.Range(arrSldNames)


All methods that are available for a single slide can be applied to the
SlideRange.
One particular use of this object is for deleting multiple slides that meet specific
criteria. If we loop through the slides and then check and delete specific ones, it
is not guaranteed to delete all slides. However, we can loop through the slides,
collect their indices/names and then generate a SlideRange. We can then delete
the entire SlideRange.

16.2.7 Shape
Any item that is placed on a slide becomes a Shape on that slide. The item could
be an image, a table or a text box that holds some text.
The syntax for declaring a shape object is as follows:
Dim shp As PowerPoint.Shape

Properties
1. Top: This property holds a numeric value. It represents the distance from the
top edge of the shape to the top edge of the slide.
2. Left: This property holds a numeric value. It represents the distance from the
top edge of the shape to the top edge of the slide.
3. Height: This property holds a numeric value. It represents the height of the
shape measured in Points.
4. Width: This property holds a numeric value. It represents the width of the
shape measured in Points.

Properties 1 to 4 all have the same Syntax. The following lines of code
illustrate the use of these properties

Dim shpTop As Double For pasting a copied Chart
shpTop = shp.Top Read the Top property of a Shape
shp.Left = 100 Set a numeric value for Left
property
shp.Width = shp2.Width Match width to that of another
Shape
shp2.Top = shpTop Match Top to a value stored in a
variable

We will never set the values of these properties as shown in ‘c’. When we
create the base file (discussed in section 16.4) we will let a macro place the
shapes at their default location. We will then manually reposition and resize
the shapes as required.

The macro that updates the contents will first use the syntax shown in ‘b’ to
read the value into a variable. The existing shape will then be deleted and
replaced with the updated content. Then we will use the syntax shown in ‘e’
to set the property values

5. Name: This property holds a string containing the name of the shape. We
should give a unique and meaningful name to any shape on our slides that we
want to update using our macros. The following line of code sets the name of
a shape
shp.Name = "tblSales"

The following line of code retrieves a particular shape from the Shapes
collection (described next) of a slide.
Dim shp As PowerPoint.Shape
Set shp = ppt.Slides("Sales").Shapes("tblSales")

6. TextFrame, TextRange & Text: These properties relate to a shape that can
contain text. For example, the area on the slide that holds the title of the slide
is such a shape. We can set the text in such area with the following line of
code
shp.TextFrame.TextRange.Text = "hello world"

Methods
1. Delete: This method deletes a specific shape from a slide. We will use it to
delete existing data before we add new data to a slide. The syntax is:
shp.Delete

16.2.8 Shapes collection


This collection holds all the shapes places on a slide. A specific shape can be
accessed using either the index of the shape or its name.
The following line of code retrieves a particular shape from the Shapes
collection of a slide.
Dim shp As PowerPoint.Shape
Set shp = ppt.Slides("Sales").Shapes("tblSales")

Methods
1. PasteSpecial: This method pastes contents from the Windows clipboard into a
slide. Contents are pasted as shapes. The method returns a reference to a
ShapeRange object which is a collection that contains the pasted contents as
shapes. We will see the ShapeRange object in the next section. The syntax is
as follows:
sld.Shapes PasteSpecial(DataType, DisplayAsIcon, _
IconFileName,IconIndex, IconLabel, Link)

All the arguments are optional. However, the ’DataType’ argument is of


special importance and we will discuss that in a short while.

’DisplayAsIcon’, ’IconFileName’, ’IconIndex’, ’IconLabel’ arguments are
applicable only if we want to display the pasted items are icons. These are not
applicable to our case.

The ’Link’ argument is applicable only if we want to link to the content in our
workbook. In this case, updating with a macro is not advisable. Linking, in
PowerPoint, works in the same way as described in section 15.1 for Microsoft
Word.

The ’DataType’ argument takes one value from the following list:

ppPasteBitmap ppPastePNG ppPasteGIF
ppPasteHTML ppPasteJPG ppPasteEnhancedMetafile
ppPasteShape ppPasteDefault ppPasteMetafilePicture

However, not all values are useful when transferring data from Excel to
PowerPoint. We will see the most useful ones here. If interested, the reader
can try the other to experiment with the other values. The two most useful
values are:

ppPasteBitmap: For transferring images. We will use this when we
transfer a Chart from Excel.
ppPasteHTML: For transferring tabular data.

These two values preserve the formatting when data is transferred and cause
minimum distortion of the pasted contents. Here is the syntax for using this
method:
Dim sr As PowerPoint.ShapeRange
'//** For pasting a chart copied from Excel **//
Set sr = sld.Shapes.PasteSpecial(ppPasteBitmap)
'//** For pasting a Range copied from Excel **//
Set sr = sld.Shapes.PasteSpecial(ppPasteHTML)

Things to note when using PasteSpecial:
a) Before we use PasteSpecial we should copy the required Excel chart or
Range using the ‘Copy’ method of the ChartObject or Range objects.

b) The method returns a ShapeRange object which, as we will see in the
upcoming section 16.2.9, is a group of individual Shapes. To access a
particular piece of content we should use the ‘Item’ method of this object
and indices of the shapes. The first object in the collection will have index
1.
In continuation with the code provided earlier, a particular piece of pasted
content can be accessed as follows:
Dim sr As PowerPoint.ShapeRange
Dim shp As PowerPoint.Shape
'//** For pasting a copied Chart **//
Set sr = sld.Shapes.PasteSpecial(ppPasteBitmap)
Set shp = sr.Item(1)


c) Although multiple items can be copied and pasted, we should ensure that
we copy & paste only one item at a time. This ensures that the pasted item
is readily accessible at index position 1.

d) When a tabular Range is pasted using this method, the pasted data is
slightly out of alignment especially when a table is pasted across multiple
slides. In such cases, slight manual realignment may be required after the
macro is executed.

2. AddTextBox: This method adds a TextBox (an area that holds text) on a
slide. The textbox is added as a new Shape. The method returns a reference to
newly added shape. This shape has the TextFrame and TextRange properties
discussed in the section on Shape. The syntax for this method is as follows
(the variable shp is declared as a Shape object):

Set shp = sld.Shapes.AddTextbox _
(Orientation, Left, Top, Width, Height)

Bad news first. All the arguments are required. Also the ’Orientation’
argument is highly dependent on the language support installed on individual
computers.

The good news is, these arguments (in fact this method itself) are required
only for the first time when we are creating our base file (described in section
16.4).

For the ’Orientation ’ argument, we have one value that works for most
purposes. This value is ’msoTextOrientationHorizontal ’.

For the other arguments, we can initially assign arbitrary, but meaningful
numeric values, and then manually position and resize the textbox in our base
file. The macro that updates the elements doesn’t even need to know these
values. The following line of code adds a text box to a slide.

Set shp = sld.Shapes.AddTextbox _
(msoTextOrientationHorizontal, 100, 100, 100, 100)

Things to note when creating a TextBox:
a) Before we use PasteSpecial we should copy the required Excel chart or
Range using the ‘Copy’ method of the ChartObject or Range objects.

The method returns a ShapeRange object (which we will see in the upcoming
section 16.2.9) which is a collection of individual Shapes. To access a
particular piece of content we should use the ‘Item’ method of the collection
and indices of the shapes. The first object in the collection will have index 1.

Range: This method returns a ShapeRange object the details of which we
will see in the next section. For now, we will just see the syntax for creating
the Range.
Dim srMyShapes As PowerPoint.ShapeRange
fSet srMyShapes = sld.Shapes.Range(arrShpNames)
Or
Set srMyShapes = sld.Shapes.Range(arrShpIndices)

arrShpNames: is an array of strings. The strings are names of the shapes that
we want to group in the ShapeRange

arrShpIndices: is an array of Long integers that are the indices of the shapes
that we want to group in the ShapeRange

At times we may have to create ShapeRange having just a single Shape.

16.2.9 ShapeRange
This object represents a group of specific shapes present on a slide. Unlike the
Shapes collection it is not a collection of all shapes present on a slide. We can
choose which slides we want to add to the ShapeRange.
Following lines of code create a ShapeRange with three slides. First using shape
indices and then using slide names.
Dim srShapeGroup As ShapeRange
Set srShapeGroup = sld.Shapes.Range(array(1,3,5))
Dim arrShpNames as Variant
arrSnpNames = Array("shpSales", "shpRevenues", "shpInventory")
Set srShapeGroup = sld.Shapes.Range(arrSldNames)


We will not use this object extensively. However, we need to know it because the
PasteSpecial method that is used to transfer data from Excel to PowerPoint,
returns this object.
Methods
1. Item: This method returns a single shape from the group of shapes that is
held in a ShapeRange. This is the most important method of this object when
it comes to data transfer between Excel and PowerPoint. The syntax is:
Dim shp As Shape
Set shp = srShapeGroup.Item(idx)

The argument idx is either an index (integer) or name(string) of a particular
shape.

2. Delete: This method deletes all the shapes present in the ShapeRange. Syntax
is:
srShapeGroup.Delete
3. Copy: This method copies all the shapes in the ShapeRange into the
clipboard. Then these shapes can be pasted in the same or another
presentation or Slide. The syntax is
srShapeGroup.Copy

16.2.10 Windows and ViewType


These are not independent objects by themselves but are properties that can be
accessed via the Presentation object.
We will not need these properties extensively. However, when used together they
serve one important function for our macros. Let’s see what these properties
stand for and then we will write code to work with the in our macros.
Each presentation file is opened in its own window. We will need just one
window of any presentation and we can access the required using the following
syntax
ppt.Windows(1)

Before we paste some content or a slide it is better to activate that window. The
code for activating a window is:
ppt.Windows(1).Activate

Each window has a ‘ViewType’ set at any time that dictates what we see in the
window. For example, the ‘Outline’ view or the ‘Normal’ view is usually used
for manual editing. These views can be set from the View ribbon.
However, following two views are more useful when working with VBA. We
will switch to the appropriate views when we do the concerned operations.
1) ‘Slide’ view: where only one slide is visible in the whole window. This view
is best to paste, align and set the sizes of shapes. We will switch to this view
whenever we want to paste content like chart or table to a slide.
ppt.Windows(1).Activate

ppt.Windows(1).ViewType = ppViewSlide

2) ‘Slidesorter’ view: where a group of slides in the presentation occupy the


entire window. At any given time, depending on the zoom level, we can see a
group of four to six slides on screen. This view is best used when we want to
copy a slide and paste it in the same file.
ppt.Windows(1).Activate

ppt.Windows(1).ViewType = ppViewSlideSorter

Once our macro has made the required changes, we will change the view back to
‘Normal’. The value for ViewType will be ’ppViewNormal’.
16.3 Using an already open presentation file
If we declare a PowerPoint application using the ‘New’ keyword, and if
PowerPoint is already running, we will receive a reference to the running
application.
However, if we try to open a file that is already open, we get a reference to a
read only copy of the file. Although our macro will execute without error and all
the updates will be made, when our macro tries to save the changes, macro
execution halts with a prompt to save the file with a different name.
To work around this problem, we will do the following:
1. Open the file any ways and obtain a reference to the file
2. Check the ‘ReadOnly’ property of the file.
3. If file is read only, meaning it is already open, close the newly opened version,
and then fetch the already opened version from the Presentations collection.
16.4 Creating the base file
In case of PowerPoint we will first have to write a macro that will aid us in
creating the base file. We will call this macro the “Base File Macro”. Here are
the steps:
1. Create the draft version of the presentation using organizational templates
and add any data that will be updated manually (without using a macro).

2. Note down the index numbers of the slides to be updated by our macro. Do
not add the dynamic content (the content that our macro will update).

3. Create a macro that will do the following:
a) Name the slides at the index numbers that we have noted.
b) Add the tables and charts using the paste special method on the relevant
slides.
c) Add relevant text boxes and add the required text.

When the data is added for the first time, the tables and images are placed on
the slides without proper alignment.
4. Open the presentation manually and, align and resize the tables, text boxes
and images. These positions and sizes will be used by the updating macro as
reference for positioning the updated charts and tables.
Following are the names we will use in our example:

Slide 2 will be named "sldSalesSummary". It will contain a chart which, in the
Excel workbook, is named "Top5Components". In the presentation the name of
this chart will be "chtSales". This slide will also hold a table named
"tblSalesSummary".
This slide’s contents and the names are shown in figure 16.4a.
Fig. 16.4a Contents and names of slide named sldSalesSummary

Slide 3 will be named "sldCurrIssues" and will hold a table. Initially the table
will fit on only one slide. But the number of rows may increase and the table
may have to be split across multiple slides. This table will be named "tblIssues".
These names can be anything based on the content of the slides. In the example
we assume that the slide shows the list of issues that the business is currently
facing. This slide’s contents and the names are shown in figure 16.4b.

Fig. 16.4b Contents and names of slide named sldCurrIssues


Slide 4 will be named "sldRevSummary" it will contain one chart named
"chtRev" and one text box named "txtRevSummary". This slide’s contents and
the names are shown in figure 16.4c.

Fig. 16.4c Contents and names of slide named sldRevSummary

The code that follows is for the Base File Macro that will insert contents from
Excel into a PowerPoint file. Wherever appropriate, comments have been
provided to explain the steps.

Sub CreatePPTTemplate()
Dim pptApp As PowerPoint.Application
Dim ppt As PowerPoint.Presentation
Dim shp As PowerPoint.Shape
Dim txtfrm As PowerPoint.TextFrame
Dim chobj As ChartObject
Dim strRevSummaryText As String
Set pptApp = New PowerPoint.Application

strRevSummaryText = _
"The total volume of sales in the quarter” & _
“ was 50000 units" & vbCrLf & _
"This was a 10%increase Q-o-Q" & vbCrLf & _
"The revenue for the quarter was $1.25 million"



'//** Open the presentation file **//
Set ppt = pptApp.Presentations.Open _
(ThisWorkbook.Path & "\" & _
"presentation.pptx", False)
‘//The file may already be open **//
‘//** in that case, the newly opened file will be a read-only copy **//
If ppt.ReadOnly Then

'//** Close the new file and get a reference to the already open file. **//
ppt.Close
Set ppt = pptApp.Presentations("presentation.pptx")
End If
'//** Setting names of slides, adding preliminary content **//
‘//** setting name of content **//

'//** Slide 2 - chart + table – sldSalesSummary **//
ppt.Slides(2).Name = "sldSalesSummary"
Set chobj = Worksheets("StageSheet"). _
ChartObjects("Top5Components")

'//** Copy the chart **//
chobj.Copy
'//** Paste the chart **//
Set shp = ppt.Slides(2).Shapes. _
PasteSpecial(ppPasteBitmap).Item(1)
Application.CutCopyMode = False

'//** Set name of the chart **//
shp.Name = "chtSales"
Set shp = Nothing
Set chobj = Nothing
Worksheets("StageSheet").Range("O1:Q6").Copy
Set shp = ppt.Slides(2).Shapes. _
PasteSpecial(ppPasteHTML).Item(1)
Application.CutCopyMode = False
shp.Name = "tblSalesSummary"
Set shp = Nothing
Set chobj = Nothing
'//** slide 3 - expanding table – sldCurrIssues **//
ppt.Slides(3).Name = "sldCurrIssues"
Worksheets("Sheet1").Range("A1:E10").Copy

Set shp = ppt.Slides(3).Shapes. _
PasteSpecial(ppPasteHTML).Item(1)

Application.CutCopyMode = False
shp.Name = "tblCurrIssues"
Set shp = Nothing
'//** Slide 4 - chart + textbox – sldRevSummary **//
ppt.Slides(4).Name = "sldRevSummary"
Set chobj = Worksheets("StageSheet")._
ChartObjects("Top5Components")
chobj.Copy
Set shp = ppt.Slides(4).Shapes.PasteSpecial _
(ppPasteBitmap).Item(1)
Application.CutCopyMode = False
shp.Name = "chtRev"
Set shp = Nothing
Set chobj = Nothing
Set shp = ppt.Slides(4).Shapes.AddTextbox _
(msoTextOrientationHorizontal, 100, 100, 100, 100)
shp.Name = "txtRevSummary"
shp.TextFrame.TextRange.Text = strRevSummaryText
Set shp = Nothing
ppt.Save
End Sub

Once the code is executed, we have to manually resize and align the various
charts and tables on the slides. We should also apply any required formatting to
the TextBoxes so that in future updates the formatting will be preserved. Once
done, future updates can be done with another macro.
16.5 Updating the base file with macros
In this section we will put together the code that we can use periodically to
update the presentation.
Note that the code only transfers updated content to a PowerPoint file. Updates
to the data should be done in Excel using methods described in other sections of
this book.
Once again, comments have been provided at appropriate places for explanation.
The complete code is around four pages long, so it has been split up into sections
based on functionality. All these lines can be part of the same sub-procedure or
function.
For the code listed in sections 16.5.1 to 16.5.4. the names (of slides, charts,
tables) and objects have the same meaning as in section 16.4.

16.5.1 Transfer text to a specific location


The approach would be to:
1. Generate the required text, either by writing it directly into code or by
concatenating strings and formatted numeric values.
2. Get a reference to the relevant text box that holds the text.
3. Replace the text
In the code that follows, the variables ’dblQoQRise’ and ’dblQtrRev’ can be
either calculated or retrieved from a worksheet cell. Furthermore, ’ppt’, ’sld’ &
’shp’ are respectively the Presentation, Slide & Shape objects.
'//String holding text
strRevSummaryText = _
"The total volume of sales in the quarter” & _
“ was 60000 units" & vbCrLf & _
"This was a “& dblQoQRise & ”% increase Q-o-Q" & vbCrLf & _
"The revenue for the quarter was $” & dblQtrRev & “million"
'//** Get reference to slide holding the textbox using the slide name **//
Set sld = ppt.Slides("sldRevSummary")
'//** Get reference to shape holding the textbox using the shape’s name **//
Set shp = sld.Shapes("txtRevSummary")
'//** Replace the text. Refer to section 16.2.7 for details of these properties
**//
shp.TextFrame.TextRange.Text = strRevSummaryText

16.5.2 Transfer data from an Excel Range


Let us now see the code to transfer data from a rectangular Range from Excel to
a table on a slide in PowerPoint.
The approach would be to:
1. Get a reference to the relevant Shape object that holds the table.
2. Read the current position (Top, Left) and Size (Height, Width) of the shape
and store these in variables.
3. Delete the existing shape.
4. Copy the Range from Excel.
5. Use PasteSpecial method to paste the table into the Slide and get the
reference to the new Shape object that is generated.
6. Set the position and size of the new shape to the values stored in step 2.
7. Set name of the new shape. This is required because in step 3 we delete the
existing shape and the new added shape does not have a name. The name
will be same as that of the existing shape so that we can execute the same
macro again when we create the report next time.
In the code that follows, ’ppt’, ’sld’ and ’shp’ are respectively the Presentation,
Slide & Shape objects.
'//** Variables holding size and position **//
Dim shpTop, shpLeft, shpHeight, shpWidth As Long

Dim shpRng as PowerPoint.ShapeRange
'//** Reference to Slide **//
Set sld = ppt.Slides("sldSalesSummary")
'//** Reference to Shape **//
Set shp = sld.Shapes("tblSalesSummary")
ppt.Windows(1).Activate
ppt.Windows(1).ViewType = ppViewSlide
'//** Read Top/Left position of the Shape **//
shpTop = shp.Top
shpLeft = shp.Left
'//** Read Height/Width of the Shape **//
shpHeight = shp.Height
shpWidth = shp.Width
'//** Delete existing Shape **//
shp.Delete
Set shp = Nothing
'//** Copy Excel Range **//
Worksheets("StageSheet").Range("O1:Q6").Copy
'//** Paste Excel Range into the slide and **//
‘//**get a ShapeRange object containing a single Shape **//
Set shpRng = sld.Shapes.PasteSpecial(ppPasteHTML)

'//** Reference to the new Shape using **//
‘//** Item method of ShapeRange **//
Set shp = shpRng.Item(1)
'//** Set Top/Left position of the Shape using stored values **//
shp.Top = shpTop
shp.Left = shpLeft
'//** Set Height/Width of the Shape using stored values **//
shp.Height = shpHeight
shp.Width = shpWidth
'//** Set name of the Shape **//
shp.Name = "tblSalesSummary"

16.5.3 Transfer a chart


Let us now see the code to transfer a chart from Excel to a Slide in PowerPoint.
The approach would be to:
1. Get a reference to the relevant Shape object that holds the chart.
2. Read the current position (Top, Left) and Size (Height, Width) of the shape
and store these in variables.
3. Delete the existing shape.
4. Copy the Chart from Excel.
5. Use PasteSpecial method to paste the chart into the Slide as a bitmap and, get
the reference to the Shape object that is generated.
6. Set the position and size of the new shape to the values stored in step 2.
7. Set name of the new shape. This is required because in step 3 we delete the
existing shape and the new added shape does not have a name. The name
will be same as that of the existing shape so that we can execute the same
macro again when we create the report next time.
In the code that follows, ’ppt’, ’sld’ & ’shp’ are respectively the Presentation,
Slide & Shape objects.
Notice also how we have avoided the use of a separate ShapeRange object by
using Item method directly after PasteSpecial method




'//** Reference to Slide **//
Set sld = ppt.Slides("sldSalesSummary")
'//** Reference to shape holding the chart **//
Set shp = sld.Shapes("chtSales")
ppt.Windows(1).Activate
ppt.Windows(1).ViewType = ppViewSlide
'//** Read Top/Left position of the Shape **//
shpTop = shp.Top
shpLeft = shp.Left
'//** Read Height/Width of the Shape **//
shpHeight = shp.Height
shpWidth = shp.Width
'//** Delete existing Shape **//
shp.Delete
Set shp = Nothing
'//** Reference to the chart in Excel **//
Set chobj = _
Worksheets("StageSheet").ChartObjects("Top5Components")
'//** Copy to the chart from Excel **//
chobj.CopyPicture xlScreen, xlBitmap
'//** Paste Excel Range into the slide and get the related Shape object. **//
Set shp = sld.Shapes.PasteSpecial(ppPasteBitmap).Item(1)
'//** Set Top/Left position of the Shape using stored values **//
shp.Top = shpTop
shp.Left = shpLeft
'//** Set Height/Width of the Shape using stored values **//
shp.Height = shpHeight
shp.Width = shpWidth
'//** Set name of the Shape **//
shp.Name = "chtSales"

16.5.4 Transfer a table with varying number of


rows
Let us now see the code to transfer data from a rectangular Range from Excle to
a table in PowerPoint. Let’s start by describing the problem in a bit more detail.
Problem:
Often in our presentation decks we have tables where the number of rows keeps
varying every time we create the tables. Sometimes there may not be any rows to
report while at other times the number of rows is large enough to spread the table
across 3 to 4 slide.
Unfortunately, unlike MS Word, in MS PowerPoint, we cannot paste a single
Excel range in a single slide and expect the table to be split across multiple
slides automatically. At the same time, we want every slide to have a header row.
Solution:
We use macros to do the following:
1. Break the full table into small sized chunks each of which would fit on a
single slide.
2. Create copies of the specific slide (one holding the concerned table) in the
presentation file. The number of copies will be based on the number of chunks
created in step 1.
3. Transfer the smaller tables to individual slides.

The full solution is quite long so it has been split into parts and each part
combines explanation of general principles as well as specific examples.

For faster and better understanding, it is recommended that the reader should
copy the code lines into the Visual Basic Editor or just a plain text file and read
through the code.

Note down the values of the variables at different stages in the iterations. Do
this for atleast 3 iterations of the while loop.

For the purpose of example, we will continue with the presentation file (called
PPT file hereafter) that we described in section 16.4. Recall that we had
mentioned that Slide 3 will be named "sldCurrIssues" and will hold a table
where the number of rows may increase and the table may have to be split across
multiple slides also that this table will be named "tblIssues".
As per the scheme of the solution, when the table is split across multiple slides,
new slides will be created to hold the parts of the table. These new slides will be
copies of "sldCurrIssues". They will be named as "sldCurrIssues_n". Where "n"
is an integer that will take values like 1, 2, 3…and so on.
We should take note of the fact that we want to use the same macro in the future
as well. Which means the macro should be able to deal with existing slides
named as "sldCurrIssues_n".

Let’s look at the different parts of the macro one by one. Please note that this
whole code can be placed inside a single Sub procedure in the same order that
it appears here.
The parts marked with ( ) are explanatory notes and can be thought of as
comments.

Sub CreateMultiSlideTable(ByRef ppt As PowerPoint.Presentation)







Delete existing slides that hold parts of the table added earlier

'//** A string variable that will hold the list **//
‘//** of all slides to be deleted, separated by a comma. **//
Dim strSlideList As String
'//** Loop through all the slides of the PPT file **//
For Each sld In ppt.Slides
'//** Check if the slide name contains "sldCurrIssues_" **//
'//** Checking for _ is very important because **//
‘//* we want to retain the first slide-sldCurrIssues **//
If InStr(1, sld.Name, "sldCurrIssues_") > 0 Then
If Len(strSlideList) > 0 Then
'//** If the string already contains some text, append a comma **//
strSlideList = strSlideList & ","
End If
strSlideList = strSlideList & sld.Name
End If
Next sld
If Len(strSlideList) > 0 Then
'//** String length is greater than 0, **//
‘//** so there were slides having an underscore ( _ ) in their names **//
'//** Create a SlideRange out of those slides and delete all of them together. **//
ppt.Slides.Range(Split(strSlideList, ",")).Delete
End If

Get a reference of the existing slide (slide 3) and shapes on that slide

Dim shp As PowerPoint.Shape
Dim shpTop, shpLeft, shpHeight, shpWidth As Single
Dim sld As PowerPoint.Slide
Dim srTemp As Powerpoint.SlideRange
Set sld = ppt.Slides("sldCurrIssues")
Set shp = sld.Shapes("tblCurrIssues")
shpTop = shp.Top
shpLeft = shp.Left
shpHeight = shp.Height
shpWidth = shp.Width
shp.Delete
Set shp = Nothing


Loop to transfer table to slides
When we first create the basefile we will get an idea of the total number of rows,
including the header row that would fit on a slide. In the code this number is
denoted by numrows. Remember that his number also include the header row.
For the example assume that this numrows = 10
We also need to know the number of columns that we want to transfer on each
slide. In the code this number is denoted by numcols. Remember that his number
also include the header row. For the example assume that this numcols = 5
The full table that needs to be split up is present in the Excel workbook on
Sheet1. We will call it “Original Table”. We will loop over the original table and
in each run, or iteration, of the loop we will copy (numrows-1) of rows from the
original table to a temporary worksheet. This copied set of rows will be called
temporary table. On the temporary worksheet the data will be pasted in cell A1.
We will use the following variables to keep track of the rows being copied and
the slides being inserted in each iteration:
· rwStart: this is the first row of data (in the Originl Table) that will be copied
to the temporary table in each iteration. It will take a different value in each
iteration.
· rwTableFirst: is the first row in the original table. (For our example we
assume this to be row 1, meaning the data starts at row 2)
· rwTableLast: is the last row in the original table.
· colTableFirst: is the first column in the original table. (For our example we
assume this to be column 1)
· slidecount: is an integer variable that holds the number of the next slide that
will be added.

Figure 16.5.4 shows the Original Table and how the different variables will
vary with each iteration.

Ø Initialize the variables before the first iteration.
Set wksMain = ThisWorkbook.Worksheets("Sheet1")
Set wksTemp = ThisWorkbook.Worksheets.Add
rwTableFirst = 1
colTableFirst = 1
‘//** First row of data to be copied. **//
rwStart = rwTableFirst + 1

rwTableLast = wksMain.Cells(wksMain.Rows.Count, _
colTableFirst).End(xlUp).Row
ppt.Windows(1).Activate
ppt.Windows(1).ViewType = ppViewSlide


Ø This Boolean variable controls the loop. When we want to stop the loop, we
will set this to False.
bKeepLooping = True
Ø Assume initially that no new slides nave to be added

slidecount = 0
Ø Start the loop
While bKeepLooping

Ø Consider the first iteration of the loop. Starting at the first row of data in the
Original Table (this row is just below the header row, ’rwTableFirst’, in our
example it is row number 2), if we have to copy 9 rows ’(numrows – 1)’, we
can count and see that the last row of the copied data will be 10. A bit of
arithmetic shows that in each iteration, the last row of data to be copied is
given by (putting the values of ’rwStart = 2’ and ’numrows = 10’ should
give us 10 ) :
(rwStart + (numrows - 1) - 1)

At the end of the loop, this number will be either greater than or equal to the
last row of the Original Table. That is when we stop the loop.

If (rwStart + (numrows - 1) - 1) >= rwTableLast Then
numrows = (rwTableLast - rwStart + 1) + 1
bKeepLooping = False
End If


Fig 16.5.4 The ‘Original Table’ to be transferred to PowerPoint

Ø Clear the temporary table on the temporary sheet
wksTemp.Range("A1").CurrentRegion.ClearContents

Ø Copy the header row to the temporary sheet. Start at the first row & first
column of the Original Table and using the Resize method create a range
having 1 row and ’numcols’ columns.
wksMain.Cells(rwTableFirst, colTableFirst). _
Resize(1, _ numcols).Copy (wksTemp.Cells(1, 1))

Ø Next we start at the first column and row number ’rwStart’ of the Original
Table and using the Resize method create a range having ’ (numrows – 1)’
rows and ’numcols’ columns.
wksMain.Cells(rwStart, colTableFirst). _
Resize(numrows - 1, _ numcols).Copy (wksTemp.Cells(2, 1))

Ø If another slide has to be added, copy the last added slide, paste it just after the
last added slide, delete the table on this new slide, set the name of the new
slide to "sldCurrIssues_" followed by the slide number. Reference to the last
added slide is held in the variable sld.
Whenever a new slide is pasted, the variable is reset to refer to the new slide.
If no new slide is required, then the variable is set to the single reference slide

If slidecount > 0 Then
'//** additional slide required **//
'//** Switch to slide sorter view **//
ppt.Windows(1).ViewType = ppViewSlideSorter
'//** activate the slide to be copied **//
sld.Select
'//** make a copy of current slide object **//
sld.Copy
Set srTemp = ppt.Slides.Paste(sld.SlideIndex + 1)
Set sld = Nothing
Set sld = srTemp(1)
sld.Name = "sldCurrIssues_" & CStr(slidecount)
sld.Shapes("tblCurrIssues").Delete
Set srTemp = Nothing
'//** change the view back to SlideView **//
ppt.Windows(1).ViewType = ppViewSlide
Else
Set sld = ppt.Slides("sldCurrIssues")
End If



Ø Copy the newly pasted range from the temporary sheet and paste it in the new
slide. Set the name of the new table (on the slide).
Set rng = wksTemp.Range("a1").CurrentRegion
rng.Copy
Set shp = sld.Shapes.PasteSpecial(ppPasteHTML).Item(1)
Application.CutCopyMode = False
shp.Name = "tblCurrIssues"
Set rng = Nothing
Set shp = Nothing


Ø For the next iteration - Increase the slide count and calculate the start of the
new row.

slidecount = slidecount + 1
rwStart = rwStart + (numrows - 1)

Ø Notice that by this time the variable ’bKeepLooping’ may acquire a value of
False and the loop would end. The new values of ’rwStart’ and ’slidecount’
will not be used in that case.
In case ’bKeepLooping’ is still True move to the next iteration.
Wend

Set sld = Nothing

Ø Once the loop ends, we enter a second loop. Here we iterate through all the
slides that have "sldCurrIssues" in their name and set the size and position of
the table contained on them to the values we stored earlier.

For Each sld In ppt.Slides
If InStr(1, sld.Name, "sldCurrIssues") > 0 Then
Set shp = sld.Shapes("tblCurrIssues")
shp.Top = shpTop
shp.Left = shpLeft
shp.Height = shpHeight
shp.Width = shpWidth
Set shp = Nothing
End If
Next sld
Set sld = Nothing
ppt.Save
End Sub



Note: Depending on the complexity of the tables, some of the new slides may
not have perfect alignment. Some manual adjustment may be required which can
be done when checking the output.
Chapter 17
Microsoft Query & Databases
In this chapter we will look at a technique that Excel provides us to retrieve data
from a variety of databases into our Excel file. The concept we are going to
discuss is called ‘Microsoft Query’ (MS Query).
The first section of this chapter describes some very elementary database
concepts. It is written for readers who have absolutely no familiarity with
databases. Readers who are aware of concepts related to databases can safely
skip that section. However, reading it might provide some pointers to effectively
pulling data into Excel.
We will then move on to working with MS Query. The steps involved are as
follows:
1. Launch MS Query
2. Connect to the required database.
3. Specify the data to be retrieved (forms the bulk of this chapter)
4. Refresh the data at a later date (we will cover both manual & automatic
modes).
5. Launch our macros automatically when data is refreshed in order to process
the data further.
We will cover steps 1, 3, 4 and 5 in order first. This is becase these steps are not
dependent on the database we are connecting to. For illustrations we will use MS
Access however the steps are the same for any other database.
Step 2 depends on the specific database we are connecting to and will be covered
in the last few sections. In those sections we will see how to connect to some
widely used databases. Of special interest is the section on MySQL (pronounced
as ‘My Sequel’) database because Microsoft Office does not ship with a default
bridge (technically called a ‘Driver’) for MySQL. However, considering that a
large number of websites today run on WordPress, which uses MySQL, it should
provide something of value to readers who own WordPress websites.
There will be very few lines of code in this chapter. Why? For the following
reasons:
a) We cannot write code for interacting with most of the databases unless we
know a little bit of Structured Query Language, aka. SQL (pronounced as
'sequel').

SQL is a special language used to extract, as well as change the data residing
in a database. It has its own syntax and its own set of keywords. Furthermore,
SQL syntax varies slightly between different databases. Learning SQL is
hence out of the scope of this book.

b) If we absolutely have to write code, there are much better options available.
However, these require use of some specialized programming concepts like
ADO (ActiveX Data Objects) or DAO (Data Access Objects) which allow us
to not only extract data but also to change data in a database. However,
programming with these objects again falls outside the scope of this book.

c) Even if we use ADO or DAO (without using the features to change the data),
it would allow us only to pull certain filtered dataset from the database.
Majority of calculations, analysis and charting would still have to be done in
Excel.

17.1 Short visit to the Database world.
Most of the databases used for commercial applications belong to a family called
Relational Database Management Systems or RDBMS. Some of the famous
ones are Oracle, MySQL, Microsoft Access, Sybase, SQLLite, PostgressSQL,
DB2 and so on.
There are some special databases that employ non-relational designs but these
are used for specialized purposes like analytics or even email (Lotus Notes
“.NSF” file is an example).
We will limit our discussion to RDBMS since that is what we will most likely
encounter. Furthermore, if an appropriate connector (Driver) is provided for non-
relational databases, either by Microsoft or by an independent party, we can use
the methods in this chapter to interact with those databases. For example,
NotesSQL is a driver for Lotus Notes databases. We will discuss a bit more
about drivers in a short while.
Coming back to our topic, in an RDBMS data is stored in the form of tables, a
set of rows and columns. The columns are also called ‘Fields’.
To give a crude analogy with Excel, each database is similar to one Excel
Workbook and the tables in the database are similar to the Worksheets in a
Workbook.

Excel Vs. Databases


The major difference between Excel and a database is that the rules for
entering data in a database are lot stricter than for Excel and the fields usually
do not contain formulas.
Another difference is the the purpose. Excel is used to combine a relatively
small set of data from different sources and analyze them quickly. Often the
analysis required is adhoc and can change frequently.
Databases on the other hand are designed to store and search (at a lightning
fast speed) through several millions of rows of data (often from multiple
tables) using criteria that depends on a combination of columns across tables.
On their own databases can do very limited analysis (may be just count, sum
and average up values).


Each table holds data for a particular class of items or entities.
For example, employees of a company represent one class of entities about
which the company may want to store data. Each row will have data for one
employee. The fields would contain details like first name, last names, date of
joining, department of the employee, a unique employee number for each
employee and so on.
Another set of entities would be all the orders received by the company. Each
row would contain information about one order. The columns would contain
information like the unique ID or number of the order, the date when the order
was received, name of the sales representative who took the order, an identifier
for the customer and so on.
Querying a database
The act of interacting with a database is called ‘Querying’. Queries are
commands that a user sends to a database. As you must have rightly guessed,
queries are written in SQL (Structured Query Language).
Queries can be used for a variety of purposes. For example, updating existing
data, inserting new data, deleting data, creating new tables, deleting entire tables
and (the subject of our interest) retrieving data from a database.
Database can also have Views. Views are virtual tables or technically speaking
Stored Queries. They are instructions written in SQL that pull data from different
but related tables based on specific criteria. These data are then combined and
presented in the form of a table. Whenever a user tries to open a View, the data is
pulled on the fly and presented as a table.
Relations between tables in a database
When we pull data into Excel we may want to combine data in two or more
tables for our analysis. For this purpose, there is one important concept from
databases that we need to understand.
Consider the two tables shown in figure 17.1a Only the first few rows are shown
here. Suppose we have imported data from the Employees table into a worksheet
named “Employees” and data from Orders table into a worksheet named
“Orders”.
Fig 17.1a Employees tables

Fig 17.1a Orders Table

Notice that in the ‘Orders’ table the ‘ID’ of the sales representative is the
‘Employee ID’ of that person from the ‘Employees’ table. If we want to create a
sheet that shows the name of the sale representative against each order, we can
use a VLOOKUP function to bring in the names from the ‘Employee’ sheet. This
function would be inserted in the ‘Orders’ sheet and the first argument (lookup
value) would be the ‘Employee ID’ of the sales rep.
Notice that in this case, the employee ID is the key column or field that relates
the two tables. However, in the ‘Employees’ table this column has a unique
value in each row and uniquely identifies each row or record. Such a column is
called a Primary key column for that table. So ‘Employee ID’ column is the
primary key for the ‘Employees’ table.
In the ‘Orders’ table the column containing the Employee IDs is allowed to have
repetition. In this table the Employee ID is called a foreign key. For the ‘Orders’
table the ‘Order ID’ column is the primary key.
Here are some important points to remember:
1. A primary key column does not permit duplicate entries or blank entries
(called NULL values in database parlance).
2. If the database is well designed, even the foreign key columns will not permit
NULL values. Even orders that are entered through a website by the
customers should have an identifier to separate them from orders taken by
sales representatives.
3. A table can have more than one primary key columns. However, the primary
key is unique. In such cases, the primary key is formed by combining the
values in the multiple primary key columns. For each row, the combination of
values in each of the key columns will be the unique identifier for that row. In
such cases, the primary key for each record is called the ‘Composite Key’.
It is helpful to know what those columns are so that we don’t forget to extract
them into our worksheet. These can then be combined (may be by
concatenation) used in our lookup functions.
4. Since the primary and foreign key columns won't have blank cells they are
very useful to find the last row of the data in our worksheets.
17.2 Working with MS Query
17.2.1 Launch MS query
On the menu bar, click on ‘Data’ to show the ‘Data’ ribbon. On the ‘Data’
ribbon, click on 'From Other Sources'. We can see that several options are
already listed for specific databases like MS Access, SQL server and so on.
These work only on specific databases and sometimes have a limitation that we
would have to retrieve an entire table. We cannot select specific columns or
combine tables.
We will discuss MS Query which is a much more flexible method. Select “From
Microsoft Query” option in this menu.

A dialog box appears where we have to select the data source (the database) that
we are interested in. For purpose of illustrations we will select “MS Access
Database”. We will deal with this step in more detail later on from section 17.5
onwards. For now, just ensure that the little check box that says "Use the query
wizard to create/edit queries" is checked.
Fig 17.2.1 Dialog box to select a data source

Once we select a data source, we are presented with the first dialog box of the
Query Wizard. This interface allows us to quickly select the required table and
columns therein and also to specify filters and sorting orders on one or more
columns without going into complicated operations of MS Query.
We will first see how to include data from a single table. In a later section we
will deal wih multiple tables.

17.2.2 Single table - The Query Wizard


Step 1 - Selecting the Data
The first dialog box of the Query Wizard is shown in figure 17.2.2a. Refer to it
while reading the text in this section.

Fig. 17.2.2a Query Wizard dialog - Choose columns


All the tables in the database are listed in the left hand box (number 1).
To include a table in the extracted data, select it and click on the right arrow
button (number 2). This will bring a list of all columns of the table into the list
on the right hand side.
In databases created for commercial purposes, most tables would have a large
number of columns. We may not require all of them for our purpose. The Query
Wizard allows us to select specific columns from specific tables.
If we click on the small ‘ + ’ sign to the left of any table name, we can see a list
of all the columns in that table. From this list we can choose the ones we want.
After clicking on the column name, we have to click on the right arrow button to
include it in our selection.
To remove a column from the extracted data, select it in the right hand box by
clicking on it. Now click on the left arrow button (number 3).
To remove all the choosen columns, click on the left double arrow button
(number 4).
We can see a list of all values in a particular (selected) column by first selecting
that column name in the right hand box and then clicking on ‘Preview Now’
button (number 5). The list appears in the box marked with number 6.
Remember that clicking on ‘Preview Now’ will send a query request to the
database. This operation can take some time depending on the number of rows in
the table and the network speed in case we are accessing the database on a
server.
Once all the required columns are selected, click on the ‘Next’ button. This
opens up the ‘Filter Data’ dialog box of the query wizard shown in figure
17.2.2b
Fig. 17.2.2b Query Wizard dialog – Filter data

Step 2 - Filtering the Data


The ‘Filter Data’ dialog allows us to specify multiple criteria for one or more
columns so that the imported set of rows will contain only the records that we
want to work with. This is a useful feature because tables in a database can
contain hundreds of thousands, sometimes even millions, of rows and importing
all of them is not a very good idea.
The “Filter Data” dialog has a list of all selected columns on the left. There is a
set of dropdowns on the right that are initially greyed out. When we click on any
column name in the list on the left, the top-left dropdown on the right becomes
active and allows us to apply a filter.
The steps to apply a filter are as follows:
1. First select the name of the column in the list on the left hand side.
2. Select the type of filter (a.k.a Operator) from the dropdown on the right. This
will activate the textbox located at the right of the dropdown.
3. Enter the value to be used in the criteria in the box next to the dropdown.
We can see that most of the available filter operators are same as those found in
‘Autofilter’ feature of Excel. Let’s take a look at the ones that are different.
Where possible, we will also see how these can be replicated using the ones that
are same as the operators in Excel.
IsNull operator
Used to filter outrows where the values are missing in the concerned field. This
operator can’t be replicated with any of the other operators

Like operator
Used to filter out rows where the concerned field contains certain characters at
certain positions. Rows are filtered irrespective of the total length of the
column’s contents or other characters that may be present in the contents.
It requires the use of wild card character '%' which allows for any number of
characters. The examples will clarify the use of this operator.

Filter Applied (Entry Examples of selected Similar Excel
on the right hand box) values filter
"%dow%" 'windows', 'window', contains “Dow“
'meadow', 'black
widow'
"dow%" ‘down’, ‘Dow Jones’ starts with
“Dow"
"%dow" window, meadow ends with “Dow"

Between operator
Used to filter rows where the concerned field has value between and including
two specified numeric or date values.

Filter Applied (Entry Examples of Similar Excel filter
on the right hand selected values
box)
1000 and 3000 1010, 1254, greater than or equal to 1000
2599.56 AND
less than or equal to 3000

Notice in this case that to produce the same effect as BETWEEN we would have
to specify two different operators when using filters similar to Excel.
In operator
used to filter rows where the concerned field has value that matches one of the
values specified in a list. The different entries in the list has to be separated by
commas.
Filter Applied (Entry Examples of Similar Excel filter
on the right hand selected values
box)
'Portland', 'Seattle', 'Portland','Seattle', equals Portland OR
'Denver' 'Denver' equals Seattle OR
equals Denver
Notice that if we are using filters similar to Excel, we will have to specify as
many different filters as there are values in our list.
Step 3 - Sorting the Data
The next screen (shown in figure 17.2.2c) allows us apply a sort order on one or
more of the selected columns.
We first have to select the column name on the left side in a dropdown. Then to
the right of the dropdown we select if we want that column to be sorted in
Ascending or Descending manner.

Fig. 17.2.2c Query Wizard dialog – Sort data

Just like in Excel, the column selected first gets the priority when sorting. So if
we provide our selection as shown in figure 17.2.2d then we get the following
result.

Fig. 17.2.2d Specifying sort order


In the selected data, the rows will be first sorted in ascending order of Employee
ID with the rows with the same Employee ID all appearing together. Among
those rows, the rows will be ordered in descending order of Order ID.
Step 4 - Confirm extraction or go to main MS Query interface
The next screen shown in figure 17.2.2e allows us to do one of the two things

Fig. 17.2.2e

1. Open the main MS Query window, where we can add more tables, add
relations between tables and specify additional criteria. We will see this in an
upcoming section on multiple tables.
2. Send the data into Excel. Since we have selected only a single table we can
select this option and click ‘Finish’.
Step 5 - Present the extracted data in Excel
MS Query provides several options for presenting the extracted data. We can
save it as an Excel Table, a PivotTable or a PivotChart with PivotTable.
Fig. 17.2.2f Sending selected data to Excel

Creating a PivotTable or a PivotChart is straightforward. We are presented with a


blank PivotTable and a list of fields that are columns extracted from the
database. We can create the PivotTable or chart as usual.
In upcoming sections, we will focus more on saving the data as an Excel Table
since it allows us to add additional columns that have formulas

17.2.3 Multiple tables – True power of MS query


In step number 1 of the previous section we can select columns from more than
one table. However, in that case we must ensure that the selected tables can be
connected by one or more common columns (Refer back to the discussions on
primary key in section 17.1).
Along with the two tables shown in figure 17.1a we will use two more tables
‘Customers’ and ‘Order Price Totals’ shown in figure 17.2.4a. ‘Order Price
Totals’ is actually a View created in the database. But in MS Query it can be
treated as if it were a table.
Notice that the connecting column between ‘Orders’ and ‘Employees’ tables is
the ‘Employee ID’ field and between ‘Orders’ and ‘Customers’ tables is the
‘Customer ID’ field. Between the ‘Orders’ and ‘OrderPrice’ tables the
connecting column is the ‘OrderID’ field.
Notice that the Orders table doe not have names of the sales representative or
names of the customers who placed the orders. It also does not have the value of
the order.
Suppose we want to create a report showing which representative took orders for
which customer and the value of the orders.
Further consider that we want to show orders taken only between two specific
dates. Let us see how we can create this report using MS Query.
Proceed as per Step 1 in section 17.2.2. Although we can select multiple tables,
it is recommended that we select only one table at this stage. This should be the
table on which we want to apply the maximum number of filters.
In our case we want to apply filters on order dates so we will select the orders
table first.
The three columns that need to be selected are Order ID, Employee ID and
Customer ID, and Order Date. Follow the steps of the previous section and apply
the filters.
In step 4 select 'Open in MS Query'. This opens the main MS Query interface as
shown in figure 17.2.4a.

17.2.4 MS Query interface


Let’s quickly look at the different part of this interface that we can use to extract
data. The serial number of the part described matches the number shown in
figure 17.2.4a. For example the menu of MS Query interface is numbered ‘1’ in
the figure.
1. Menu
The menu contains the commands that allow us to work with MS Query.
We will not cover all the menu items. The most essential ones will be taken up as
and when we discuss further topics.
Fig. 17.2.4a The main MS Query window

2. Buttons or Icons
Several buttons are available that are mainly shortcuts to the menu items. We
will describe some buttons here others will be described as related topics are
discussed
Ø Auto-query button ( )
If this button is pressed, MS Query retrieves a new set of rows whenever we
make any change in any of the areas. Meaning everytime we add/remove a
column to the fetched rows area, or add/remove some criteria, MS Query will
run a new query on the database and pull a new set of rows automatically.
This is OK if we are working with a database on a local machine or we are
fetching a small set of rows over a high speed corporate network. However, for
a large number of rows fetched over normal networks it would take a long time
and MS Query may stay hung up for sometime.
We will avoid using the Auto-query mode. If Auto Query mode is on this button
appears depressed. We can click it again to turn off Auto Query mode.
Ø Query now button( )
This button is used to retrieve a new set of rows when Auto Query mode is
switched off. The query will not run automatically. We will need to click on this
button to retrieve a new set of rows after changing some criteria or
adding/removing columns.
This functionality is good because we can add all required columns and criteria
first and MS Query will then have to retrieve a relatively smaller set of rows at a
time instead of repeatedly refreshing the data.
3. Tables area
It shows all the tables that are available for inclusion in the dataset. Each table is
shown as a box with a list of all its columns.

If this area is not visible go to 'View' menu and select 'Tables'or click on the ( )
icon.
4. Criteria area
This area is used to specify the filtering criteria based on which rows will be
fetched from the database.
If this area is not visible go to 'View' menu and select 'Criteria' or click on the (
) icon
5. Fetched rows.
This area shows all the rows that are currently extracted. This area can get pretty
large based on the number of rows retrieved.

17.2.5 Selecting Data


v Adding / removing the required tables
We can bring in the tables from which we want to extract data in two ways
1. Go to ‘Table’ menu and select 'Add Table'. A box comes up with a list of all
tables available in the database. This list also has views or stored queries.
Select the table to be added and click on the 'Add' button. Do this for every
other table that has to be included.
If the required table is not visible in the list, click on the 'options' button. Make
sure the items 'Tables','Views' and 'Synonym' are selected. Click refresh. Now
the required tables/views should be available in the list.
2. Click on the ( ) icon. It will bring up the same box that appears when using
step 1. Repeat the procedure as in step 1
Every table that is brought in is displayed as a list of column names. From this
list we can select the columns that we want to be displayed or that we want to
use when specifying criteria.
v Selecting the columns to display
Columns that will be displayed in the extracted data can be selected as follows:
1. Each table has a star ( * ) at the top of every list. We can double click on this
star to include all the columns in that table in our display
2. Double click on the particular column name and it gets included in the fetched
rows area.
If auto-query mode is off, only the heading of column is visible and no data
appears in the columns. To see the data simply click on the ‘Query Now’ button.
By default, the columns get added to the extreme right of the existing columns.
to relocate any column, do the following (refer to figure 17.2.4a)
1. Click on the heading of the column to highlight the entire column.
2. Click on the column heading and while keeping the left mouse button pressed,
drag the column to the desired location in the table.
For our example, here is the initial list of columns selected in the dislay area.
Table name Fields
Customers Company
Employees FirstName,
LastName
Orders OrderDate,
ShipCity
Order Price Totals Price Total
v Deleting columns
To remove a column from the Fetched Rows area, click on the column heading
to highlight the entire column. Then press the ‘Delete’ key on the keyboard.

17.2.6 Joining multiple tables


When we want to extract data from multiple tables, we need to ensure that the
tables are related by at least one column. We will use the term “Key Columns”
for such columns.
In MS Query interface we have to explicitly specify the connecting columns.
This act of defining relations between tables is called “Creating a Join”. The
most useful join which is also the only join possible when more than two tables
are involved, is called an “Inner Join”.
In an inner join only those rows are included for which values in the key
columns match in all the tables that are involved. For example, in section 17.1
“Short visit to the Database world” we had thought of combining the Employees
and Orders table to show the names of sales reps for each order. The connecting
columns in this case were the ID column of the Employees Table and the
Employee ID column of the Orders table.
In this case if an inner join is created, only those rows will be listed where
employee IDs match in both the tables.
If there is an employee who has not taken any order, his name won't appear in
the extracted data. If there is an order where employee ID is null, that order
won't appear in the report
v Creating a join
1. Place the tables to be joined side by side.
2. Scroll the list of columns in each table till the name of the connecting column
is visible.
3. Now click on the key column in any one table.
4. With the left mouse button pressed, drag the field over to the key column in
the other table.
5. Release the mouse button.
We will notice that a line appears joining the key columns between the two
tables.
v Removing an existing join
Double click on the line. A dialog box appears. We will discuss this dialog in a
bit more detail in a later section. For now, just notice the button labelled
'Remove'. Clicking this button will remove the join.

17.2.7 Setting Criteria


Setting criteria (which is same as applying one or more filters) allows us to limit
the number of rows that are extracted from the database so that we have a
smaller dataset to work with.

v How criteria should appear in the criteria area
1. Criteria have to be specified for each column on which we want to apply a
filter. Each of these cumns should be included in the criteria area.
2. The included columns need not be displayed so its not necessary to include
them in the fetched rows area.
3. If multiple criteria have to be satisfied on the same column, that column may
have to be included multiple times in the criteria area
4. If some criteria had been specified in the Query Wizard, they will be
automatically included in the criteria area. We can review these and delete
them if required.



v Adding criteria
On the menu bar click on 'Criteria' and then 'Add Criteria' a box appears as
shown in fig 17.2.7a.

Fig 17.2.7a Add Criteria dialog box

The ‘And’ and ‘Or’ radio buttons are used to specify if the criteria have to be
satisfied simultaneously or independently.
The ‘Total’ field has a drop down where we can select the summary function that
is either already applied or will be applied to the column. This should be done
before we run the query with the new criteria.
The next drop down called ‘Field’ allows us to select the column on which we
will apply the criteria.
The next three fields are same as those described in ‘Step 2 –Filetering the Data’
of section 17.2.2 “Single table – The Query Wizard”.
The “Values” button presents a list of values available in the selected column.
We can select one or more values from the list (by holding down control key and
clicking on individual values). This is useful when we use the “equals” operator
or the “is one of' operator”.
Click on “Add” button. MS Query will create a new column in the criteria area
along with the criterion.
We can add more criteria if required. We just need to ensure that one of the
options “And” or “Or” is appropriately selected.
Once the required criteria are added, we can close the dialog box.
We need to check if the criteria added by MS Query are in the appropriate rows.
If not, select the criteria string, cut it and paste it in the correct cell.
v How the criteria are evaluated when fetching rows
1. A set of rows from all the tables that satisfy the join conditions we extracted.
Let’s call this ‘recordset’.
2. MS Query takes the first row in the criteria area. Let’s call this ‘criteria row’.
From the recordset, the rows that simultaneously satisfy the criteria in each
cell of the criteria row are selected. Let’s call this set the ‘result set’.
3. Then the second row in criteria area is taken and becomes the new criteria
row. All the rows (in recordset) that simultaneously satisfy the criteria in each
cell of the criteria row are selected. These are then added to the ‘result set’
along with the rows present earlier. If any of the rows were already added,
they are not added again.
3. Step 2 is repeated for each of the remaining rows of the criteria area. till a
final result set is formed.
4. From this result set, the columns to be displayed are fetched and all the rows
are added to the fetched rows area.
Let us see few examples, figure 17.2.7b can be used for reference

Fig 17.2.7b Specifying Multiple criteria in MS Query

We had already specified the criteria for order date column to be between 1
March and 31 March 2006.
Along with this, suppose we want to select only those orders that were shipped
to Chicago, Memphis or New York. This can be added using the 'is one of'
operator. Since this criterion should be satisfied along with the criteria on the
‘Order Date’ column, we should select the “and” option in the “Add Criteria”
dialog.
After closing the “Add Criteria” dialog, we should check if the new criterion is
in the same row as the criteria for date filters. The criterion is marked with
number (1) in figure17.2.7b
Now, suppose we want to include the rows where ‘Ship City’ has the characters
“LA” in its name. These rows should be in addition to those selected by the
previous criteria so we need to add this criterion with the “Or” option.
We will notice that MS Query will add all rows where ship city has “LA” in their
names irrespective of whether the criteria of order dates are satisfied.
So we need to add the order date criteria in the second criteria row as well. To do
this we simply select the “Order Date” criteria in the the first row, copy it and
paste in the second row in the same column. If we how hit the ‘Query Now’
button the total number of rows reduces.
The criterion is marked with number (2) in figure 17.2.7b.
Let’s add another criterion using a different method. This will be easier now that
we know how the And/Or combinations work.
Suppose we want to include rows where order date is earlier than 1st Mar 06, but
only those where the total order value is above 1000 USD.
We need a new field in the criteria area, the ‘Price Total’ column from “Order
Price Totals” table. To get the new field, go to the rightmost blank column in the
criteria area and click in the top most cell in the row labeled “Criteria Field”. A
dropdown arrow appears. Click on this the dropdown arrow and we will see a
list of all fields in all tables. The format used is [table name].[field name]
If the full name is not visible, we can widen the column by dragging the column
boundaries just like resizing a column on an Excel worksheet.
Warning: It is possible that columns in different tables would use the same name
(for example there is an ID column in the Employees table, the Orders table as
well as the Customer table), so be very careful when selecting the field. Be sure
to select the field in the correct table.
Now, we are adding a new independent criterion (one that has to be satisfied
even if the other criteria in other rows are not satisfied), so we will be doing all
the entries in a new row in the criteria area.
For entering the criteria for “Order Date”, (in the criteria area) double click in
the cell in the column of “Order Date”. The “Add Criteria” dialog box comes up.
The only difference is that the “Select column” drop down is missing since we
have already pointed to the column having the required table column. Add the
criteria as done before.
Now, in the same row, double click in the column where we selected the ‘Price
Total’ column. Enter the required criteria.
Click on ”Query Now” and we get the new set of rows that include the rows as
per the newly added criteria.
The new criterion is marked with number (3) in figure17.2.7b
v Removing criteria
To remove a criterion, just click in the cell of the criteria area, and delete the
criteria string.

17.2.8 Closing MS Query


Once we are done selecting the data and specifying the filters we can close the
ms query window (justike closing a regular window). We get the same dialog as
the Step 5 described in section 17.2.2.
17.3 Creating summaries in MS Query
MS Query can create some basic summaries for us. Before we begin we need to
understand the concept of grouping called ‘Group By’ in SQL. Readers who are
already familiarwith the ‘Group By’ can skip section 17.3.1.

17.3.1 An example in Excel


To understand the concept of grouping let us first create a summary in Excel
using a PivotTable.
v The scenario
Suppose our company sells three products, pipe, wire and fittings, in four
different regions. Each region has two sub regions A and B. We get the data on
demand from all these regions for different products. The demand can be of two
types, external demand from the end customers and internal demand from
various subsidiary and partner companies. A part of the data is shown in figure
17.3.1a.

Fig 17.3.1a Demand data

v Required output
We need to see the total demand for products in each region. Usually we would
produce a pivot table like shown in figure 17.3.1b

Fig 17.3.1b Summary to be created

The arrangement of fields is shown in figure 17.3.1c.


As we can see, in figure 17.3.1c and figure 17.3.1b, the fields are arranged along
two axes - rows as well as columns.

Fig 17.3.1c Demand data

However, MS Query cannot produce such a summary. The fields can only be
arranged along rows.
This type of summary is shown in figure 17.3.1d and the arrangement of fields
for the pivot table is shown in figure 17.3.1e
v Some terminology
We will call the ‘Product’ and ‘Region’ fields as the Objective fields because we
are summarizing for each value of region and product. The ‘Quantity’ column in
the original data which is summed up will be called the Value field. All other
fields like ‘Order Type’ and ‘SubRegion’ will be called Non-Objective fields.
Fig 17.3.1d

Fig 17.3.1e

When such a summary is produced in MS Query, the objective columns will not
have any blank cells. The final product is shown in figure 17.3.1f. Notice that the
sub and grand totals are not present because MS Query will not show such totals.
Fig 17.3.1f Final summary that will be created in MS Query

v Steps to create the final summary in Excel:


Let’s start with the base data. Basedata is similar to a PivotTable with all the
columns marked as row fields. A part of this pivot table is shown in figure
17.3.1g along with the arrangement of fields in the pivot table.

Fig 17.3.1g (left) New pivot table and (right) arrangement of fields

Notice how the fields have been grouped:


1. All the rows wherer the ‘Order Type’ is ‘External’ are together.
2. Within this, all the rows for product ‘Pipe’ are grouped together.
3. For each pipe group, all the rows in North region are grouped together.
4. For north region, all subregion ‘A’ are grouped together.
5. Then we have the respective quantities.
Now let us summarize the Quantity column. Meaning we shift it to the values
fields in the pivot table and change the total as sum as shown in figure 17.3.1h

Fig 17.3.1h (left) New pivot table with Quantity summarized and (right) the arrangement of fields

We find that the figures don't change. This is because for each of the objective
fields say ‘Pipe’ and ‘North’, the non objective fields ‘SubRegion’ has distinct
values that can't be grouped.
To refine our summary, we should either remove ‘SubRegion’ or apply a
summary on that column. Being a text field we cannot Sum or Average it. Let us
summarize it with the Count function. The new PivotTable and its layout are
shown in fig 17.3.1i.

Fig 17.3.1i New pivot table with Sub-region summarized and the arrangement of fields


We still see that the number of rows is more than required and the non-objective
field ‘Order Type’ is still present. It again has two distinct values
(internal/external) for each group of Product and Region. Since we can't Sum or
Average it, let's summarize it with he Count function. The resulting table is
shown in figure 17.3.1j. This is much closer to the required table shown in figure
17.3.1f.

Fig 17.3.1j New pivot table with Order Type summarized and the arrangement of fields

Note that we don't necessarily have to summarize the non objective fields. We
can remove them all together. Figure 17.3.1k shows the table with the non
objective fields removed. This matches the table in fig 17.3.1f




Fig 17.3.1k New pivot table with non-objective field removed and the arrangement of fields

v Key Takeaways
1. All the non objective fields should be either removed from data extracted or
some summary function should be applied on them.

2. The objective fields should have as few distinct values as possible.

17.3.2 Summary of imported data – Performance of


sales force
With the understanding of the grouping concept, let's summarize our extracted
data in ms query.
Like in Excel pivottables, MS Query provides standard summarizing functions.
These are Sum, Average, Count, Min and Max.
To summarize a column, the steps are as follows
1. Click in the column to select it in the Fetched Rows area.
2. In the buttons area there is a button ( ). Hovering our mouse over this
button shows that it is called “Cycle Through Totals”. Clicking this button
once applies the first summary function (Sum) to the selected column. The
heading of the column changes to “Sum of [field name]”. If the Auto-Query
mode is off the column may be empty, to see the data click on the ‘Query
Now’ button.
3. Clicking the ( ) button again applies the second function to the column and
the heading changes accordingly. So we can click the ( ) button five times to
apply five different functions one by one. On the sixth click the column
appears without any summary function.
Now let's create a summary in the data we extracted earlier.



Problem:
Suppose we want to produce a report to know the performance of our sales-
force. Within the period under consideration we would like to know the total
number of orders taken by each sales rep and the total value of those orders.
Solution:
We will create a summary in MS Query to give us the required metrics. The final
summary as it would appear in MS Query is as follows:

Let’s see how such a summary can be created.


Notice that in our case the objective columns are the ‘First Name’ and ‘Last
Name’ columns of ‘Employees’ table.
We can bring in the ‘ID’ column from ‘Orders’ table. We will count the total
number of order IDs for each sales rep to know the total number of orders taken
by that person. So we use the ( ) button to apply the ‘Count’ function on the
ID column. If we hit query now, the data gets refreshed.
However, we will see that all the rows contain ‘1’, no summary is created and
the number of rows in the fetched area is the same as that without a summary. If
a summary was done, the number of rows should have reduced.
This has happened because there are other columns which have distinct values
and cannot be grouped. One such column is the ‘Price Total’ column where each
order has a different value.
Since we want to show this column in our summary we will use the ( ) button
to apply the ‘Sum’ function to this column.
Again we will find that no summation happens and we have the same number of
rows. We see that the ‘Order Date’ field has unique values for most of the rows.
Let’s see how we can solve this problem.
We can remove the ‘Order Date’ column from the fetched area. Remember that
we can still have it in the criteria area to apply filters. However, for now let's
keep it in the fetched area but with a summary function applied.
We can't apply the Sum or Average functions because it is not a numeric field.
We can count it but the result would be about same as that of counting the order
ID field (unless two orders were taken on the same day).
To get meaningful information, let's apply the ‘Min’ (minimum) function. It
would give us an idea about the time it took for the sales rep to get a particular
volume of orders. Min function would show the date of the first order taken by a
sales rep during the period under evaluation.
Now the number of rows would reduce slightly but still the ‘Company’ field
prevents effective grouping. Again we can remove this column. But we will
apply the Count function. It will give us some information about how many
customers a sales rep chased to get a particular volume of business.
This is how the final result will look like. We can see that the result matches the
one we displayed at the beginning.

17.3.3 Applying criteria to summarized rows


Often we have to filter rows based on the summarized value of the columns. Let
us see how we can add criteria for summarized rows.
Note that criteria for summarized rows should be added only through the
‘Criteria’ menu.
Open the ‘Add Criteria’ dialog as described in section 17.2.7. Now refer to the
figure 17.3.3.

Fig 17.3.3 Adding a criterion for summarized field

In the ‘Add Criteria’ dialog we had a field called ‘Total’. We can select the
summary function we want to apply and then specify a criterion for that
summarized value
For example, suppose we want to find sales reps who have gathered less than or
greater a certain number of orders.the figure 17.3.3 shows how the criterion is
set
It should be noted that for applying such criteria the fetched rows should already
be summarized. Again, the field on which we set the criteria need not be in the
display. For example, in our case, we can remove the ‘Count of Company’ field
from the Fetched Rows area but still apply a filter criterion on ‘Count of
Company’.
17.4 Working with imported data
The data that is extracted from databases and placed on our worksheets is
referred to as imported data. In this section we will see some basic operations
that we can perform with the imported data.

17.4.1 Refreshing the data manually


The data in the database may change over time after we import it. To reflect the
change in our worksheet we need to update the imported data.
To manually update the imported data, we just right click on the related table or
pivot table, and select 'Refresh' in the pop-up menu.

17.4.2 Auto-Refresh at fixed intervals


To update he data automatically at regular intervals, do the following (refer to
figure 17.4.2a):
1. Click in any one cell in the imported data.

2. In the ‘Data’ ribbon look at the connections group and click on 'Properties'.
This brings up the 'External Data Properties' dialog.

Fig 17.4.2a (Left) Connections group on the Data Menu. (Right) Data Properties dialog.

3. Near the connection name (disabled) there is a small icon (shown by number
1 in figure 17.4.2a right) this brings up the ‘Connections properties’ dialog
box shown in figure 17.4.2b. In this dialog, click on the tab labelled ‘Usage’.

4. There is a check box called 'Refresh Every' marked by number (1). Activate it
by placing a check mark in it. Then enter a positive whole number in the small
box next to it. This number is the interval in minutes between two refresh
cycles. Meaning if we enter '5' the data will get refreshed every 5 minutes as
long as the connection to the database is maintained.
There is another check box named 'Enable Background Refresh’. Background
refresh means that we can keep working with Excel while the data is being
pulled from the database.
Remember that if our query is extracting a large number of rows and columns or,
if it is a complex query having many criteria and summary functions, then it can
take considerable amount of time for the data to get refreshed. The time taken is
even more if we are accessing the database over a network. If background
refresh is not enabled, then during the refreshing process Excel will hang up and
we won't be able to do any work.
To have Excel available during the data refresh cycle place, a check mark in the
'Enable Background Refesh' check box. However, we should be careful that
while data is being refreshed, we do not carry out any processing that depends
on the updated data or else, our results may be incorrect.
Fig 17.4.2b Connection Properties dialog box. ‘Usage’ tab.

17.4.3 Edit an existing query


To edit an existing query, open the connection properties dialog box by
following steps (1) to (3) in Section 17.4.2. Then click on the tab named
'Definitions' (refer to figure 17.4.3a)
Be very careful with this tab. there are several boxes that have text. These hold
the connection string, a piece of code defining how we are connecting to the
particular database and the Command text, which is the query in SQL language.
We should ensure that we don't accidently make any change to either of these
boxes.
If we accidently do something in any of these boxes we should immediately
click on the ‘Cancel; button and start over again.
Click on the 'Edit Query...' button. For multi table queries, ms query would
display a message saying that the query cannot be edited in query wizard. Click
OK in this box.
We will be presented either with the Query Wizard (in case of simple single table
queries) or the main MS Query window. From here we proceed as per the steps
described in sections 17.2.

Fig 17.4.3a Connection Properties dialog box. ‘Definition’ tab.

17.4.4 Associating a formula


v When the imported data is displayed as a table
In this case we can add a column immediately after the last column of the table.
This column can contain static values that we can type in. However, the real
advantage is achieved when this column contains formulas that use imported
data values as arguments.
Formulas can be typed in the usual way as done on normal Excel worksheet. The
references for table columns are automatically filled in.
In the figure 17.4.4a the column G holds a formula. Since it is immediately after
the last column of the table, it becomes part of the table. This has the advantage
that when we update the data in the table from the database, any new rows that
are added will cause the formula column to expand accordingly. Similarly, if any
rows are removed, the formula column will contract automatically.
Fig 17.4.4a Checking that the column with the formula is part of the Table

To be certain that the formula column is actually associated with the table, we
can do a check as follows
· Click in any of the cells of the table, this should show the Design tab in the
main menu.

· In the Design ribbon, look at the “Properties” group and click on “Resize
Table” (shown in the adjacent figure)
This does two things (both are shown in figure 17.4.4a)
1. The Resize Table dialog box is shown. We should ensure that the data range
shown in that box includes all our formula columns.
2. The boundary of the table gets highlighted with “Walking Ants” as shown in
the figure. Our formula columns should be within this boundary.
If our formula columns are not part of the table, we can just type in the address
of the new range (that include our formula columns) in the Resize Table dialog
box. This new range would now include the columns with formulas.
v When the imported data is displayed as a pivot table
it is not advisable to have formulas immediately next to a pivot table. Why?
Because, while a table.can expand and contract along its rows when new data is
added, a pivot table can expand and contract along its columns as well. So if the
number of columns increases after refresh, it would wipe out the existing
data/formula columns, if it contracts, blank columns would appear.
It is better to keep the pivot table as a standalone range of data and then use
methods described in chapter 5 to fetch data from the PivotTable and do
calculations with the returned data.
17.5 Query data and VBA
In this section we will look at ways to interact with the imported data using
VBA. Note that macros will not be used to fetch the data. Only to operate on
data that is already imported.

17.5.1 Setup for VBA


First thing we should do is to give a meaningful name to the Table or PivotTable
of the imported data. This will be useful for addressing the table in macros. The
steps for naming a Table or PivotTable are described in sections 9.2 and 5.1.1
respectively.

17.5.2 Refreshing the data with VBA


For a PivotTable refreshing is pretty straightforward. Once we retrieve a
PivotTable object using the name of the PivotTable, we just use the
‘RefreshData’ method as described in Chapter 5.
For a table we proceed as follows:
We know that a table is a ListObject as described in 9.5 “Using Table and
column Names in VBA”. If data is retrieved from a database with a query, it is
held in an object called a QueryTable. This object is accessible using the
“QueryTable” property of the ListObject.
Once we have the QueryTable object we can refresh the data using its “Refresh”
method. The Refresh method takes one argument, a Boolean named
“BackgroundRefresh”.
If set to TRUE, background refresh is enabled for the current operation. As soon
as Excel is able to connect to the database, control returns to our macro and the
next line of code is executed irrespective of whether the complete set of data is
returned or not.
If set to FALSE, background refresh is disabled for the current operation.
Control does not return to our macro until data is returned from the database. We
will use this mode since there is no use doing any further processing till we have
updated data.
Assume we have a worksheet (Sheet3) that contains extracted data in a table
named "QueryFromAccess”. The code would be as follows:
Dim qt As QueryTable
Dim wks As Worksheet
Dim lo As ListObject
Set wks = Worksheets("Sheet3")
Set lo = wks.ListObjects("QueryFromAccess")
Set qt = lo.QueryTable
qt.Refresh(False)

17.5.3 Trigger a macro on data refresh


Problem:
Many a times we would like to automatically start some processing once the
queried data is refreshed either manually, as per a set schedule or by a macro.


Solution:
When the queried data gets refreshed, the range of cells containing the data
changes. A change in the cells triggers the ‘Change’ event of the worksheet. We
can trigger our own macro from within the ‘Change’ event.
To get the procedure for the Change event, double click on the module of the
concerned worksheet in the Project Explorer part of the Visual Basic Editor. This
opens the code window for the worksheet.

In the left drop down select ‘Worksheet’. In the right side drop down select
'Change'. Excel will automatically create an empty sub procedure for the the
change event.
We can see that the sub procedure has one argument called ‘Target’. This
argument is passed to the sub procedure and is available for use to the code
inside the sub procedure. It represents the range that has undergone a change,
either manually by the user or through some macro.
The problem is that the ‘Change’ event would get triggered if any cell on the
worksheet undergoes a change. So we need to confirm that the ‘Target’ argument
actually represents the range of the queried data. Let’s see how we can do this
v When queried data is displayed as a pivot table
Every PivotTable has a property called “TableRange2”. It represents the entire
area occupied by the PivotTable inclusive of the page filter fields.
For the PivotTable shown in figure17.5.3, TableRange2 will be the range
$A$1:$C$8. The ‘Page Filter’ fields are the cells A1:B1.

Fig 17.5.3 Sample PivotTable

Notice that this range doesnt include any formulas that may have been written in
adjcent columns.
This entire range gets refreshed when we refresh the data in the PivotTable.
So in our Change event if the address of the ‘Target’ argument is same as the
address of ‘TableRange2’ of our pivot table, it is confirmed that the table has
been updated. Thecode in the Change event will be as follows:
Private Sub Worksheet_Change(ByVal Target As Range)
If StrComp(Target.Address, PivotTables("pvt"). _
TableRange2.Address) = 0 Then
‘//** Code here…… **//
End If
End Sub

v When data is displayed as a table
The range that changes can be obtained using the ‘ResultRange’ property of the
‘QueryTable’ object.
Notice that the ‘ResultRange’ includes the extra columns that we may have
added. Hence in the ‘Change’ event of our worksheet we should compare the
addresses of ‘Target’ and the ‘ResultRange’.
Here is how the code will look like.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lo as ListObject
Dim strChkAddress as String
Set lo = ListObjects("QueryFromAccess")
strChkADdress = lo.QueryTable.ResultRange.Address
If StrComp(Target.Address, strChkAddress) = 0 Then
‘//** Code here…. **//
End If
End Sub


17.6 Connecting to MS Access
Connecting with MS Access is the easiest thing to do when using MS Query.
First launch MS Query as described in section 17.2.1. The “Choose Data
Source” dialog comes up

In the “Databases” tab, select “MS Access Database” in the list and click on the
“Browse” button.
Just browse and select the concerned MS Access file.
Important Note:
If our Access file is located on a server and has to be accessed over a network,
we would have to first map that network drive on our compouter. Then when
browsing for the Access file, select that mapped drive and look for the file.
17.7 Connecting to MySQL (ODBC +

WooCommerce)
Connecting to MySQL database is a bit tricky because at the time of writing of
this book, Microsoft does not provide a driver for MySQL.
Fortunately for us, MS Query can accept what is technically called an ‘ODBC
driver’. ODBC stands for Open Database Connectivity. It is a software bridge
that database makers provide to allow different programs to connect to their
database without knowing a lot of technical details. The catch is that after
installing the driver we need to take one more step - that of setting up an ODBC
Data source on the computer through which we want to access the database.
There are ways of access a Data source created on another computer, through a
different type of data source (a File Data source described in ‘Types of Data
sources’), but we will limit ourselves to a single computer for now.
As an example we will connect to a MySQL database on a webserver.

17.7.1 Prepping up our database connection


In order to connect to our MySQL database, we need the following information.:
1. The Database user name and password. In case of a WordPress website, we
would receive these at the time of installing Wordpress. For users who are
connecting to a non-WordPress system or to database on a separate server,
please contact your server administration team for these credentials.

2. The database server’s IP address and the Port number.

3. The database name to which we want to connect. For WordPress users, this is
provided at the time of installation. Other users would need to get in touch
with the server administration team.

17.7.2 Setting up ODBC driver and Data Source


The latest ODBC driver for MySQL can be obtained from the official MySQL
site
http://dev.mysql.com/downloads/connector/odbc/
We need to ensure that we install the correct version 32-bit vs 64-bit. The
decision depends not on the operating system but on the bit-version of Excel
installed on our machine.
To know if we are running a 32-bit Excel on 64-bit Windows operating system,
open any Excel file to launch the application. Open the ‘Task Manager’. Click
on the ‘Processes’ tab and scroll down till the entry ‘Excel.exe’ appears. If we
are running a 32-bit version, we see ‘Excel.exe *32’ listed in the processes tab.
Now we can download and install the appropriate version.
Once the driver is installed, its time to create our datasource. Here are the steps:
1. In the Windows Control Panel, open the ‘Administrative Tools’. In newer
versions of Windows, it will be under ‘System & Security’ group in the
Control Panel.

2. Scroll to locate the ‘Data Sources (ODBC)’ entry and double click on the 32-
bit or 64-bit entry as per the installed driver. This will bring up the ‘ODBC
Data Source Administrator’ dialog as shown in figure 17.7.2a.

Note that the ODBC administrators for 32-Bit and 64-Bit data sources are
different programs (.exe files).

In Windows 10, the two options are clearly marked in the ‘Administrative
Tools’ area in the Control Panel. However, in earlier versions of windows (say
Windows 7), there is only one entry for ‘Data Sources (ODBC)’.

In such case, right click on the entry and select ‘Properties’ in the pop-up
menu. The Data Source Properties dialog is displayed.
Go to the ‘Shortcut’ tab and check the path mentioned for ‘Target’.
If the path is ‘C:\Windows\system32\odbcad32.exe’, then that entry in
Administrative Tools is for creating a 64-bit data source.


To create a 32-bit data source we have to locate the correct ODBC Data
Source administrator tool.
Its file name will be ‘odbcad32.exe’ but it will be found under
‘C:\Windows\SysWOW64’.
Double click and open this file to launch the 32-bit ODBC Data Source
administrator.
3. In the ‘ODBC Data Source administrator’ (figure 17.7.2a), click on the
‘Add…’ button which will bring up the ‘Create New Data Source’ dialog box
as shown in figure 17.7.2b.
Fig 17.7.2a ODBC Data Source Administrator

4. In the ‘Create New Data Source’ dialog scroll and select the ODBC driver for
MySQL, select the driver and click on ‘Finish’. This will bring up the
‘MySQL connector/ODBC configuration’ dialog box shown in figure 17.7.2c.

Fig 17.7.2b Create New Data Source



Sometimes two different drivers are visible, one for ANSI and another for
Unicode.
Usually Unicode driver is required when we are dealing with non-english
characters. For all practical purposes ANSI driver is sufficient.
5. In the ‘Create New Data Source’ dialog scroll and select the ODBC driver for
MySQL, select the driver and click on ‘Finish’. This will bring up the
‘MySQL Connector/ODBC Data Source Configuration’ dialog box as shown
in figure 17.7.2c.

6. Enter a meaningful name for the data source in the box labelled ‘Data Source
Name’. We also have an option to provide a small description.

7. Enter the IP address of the database server and the Port number assigned for
MySQL on that server.

8. Enter the user name for the database in the box labelled ‘User’.

9. Enter the password in the box provided.

10. Click on the button labelled ‘Test’.

A message is displayed informing us if the connection is successful or not.


Fig 17.7.2c MySQL ODBC confifguration


11. If unsuccessful, we need to check the entries made in this box.

12. If the connection is successful, select the specific database in the dropdown
labelled ‘Database’.
Now the ODBC setup is done. Now when we launch MS Query (as described in
section 17.2.1) our MySQL data source will be listed in the ‘Choose Data
Source’ dialog box shown in figure 17.2.1.
We can select that data source and proceed with the techniques described in this
chapter.
17.8 Corporate databases
The techniques for connecting to databases that we have seen so far were related
to simple databases. However, when working in a corporate setting we may
have to work with complex industrial grade databases like Oracle, DB2 and
Sybase.
In such cases it is best to seek help from the database administrators in the IT
department for creating the data source.
ODBC is a general technique and can be used to connect to any database as long
as the correct ODBC driver is available. However, databases may require
additional components. For example, in case of Oracle, the ‘Oracle Client’ and
‘Networking components’ are required on the client computer (the computer
which will connect to database via Excel) and these are not available in the MS
Office installation.
Additionally, changes may also be required in the Windows firewall settings on
the client computer. People from IT department are in the best position to advise
on these matters.
Once the ODBC data source is created, the techniques of sections 17.1 to 17.5
can be applied irrespective of the database involved.
Chapter 18
Scenario and Usage of the 3-Tier
Application
Chapters 18 to 21 form the third part of this book. In this section we will see the
basics of creating a ‘Three Tier’ application in Excel. Each of these four chapters
deals with a separate portion of the application.
Our focus in these chapters would be to get a good hold on basic application
design principles and learn the techniques to create such an application in Excel.
But before we go further, let’s briefly see the function that each tier of our
application would perform.
Note: In the discussion that follows, when we say database, it doesn’t necessarily
mean a database program like Access or Oracle. A database can be any
structured store of data like an Excel Worksheet or even text files.
First tier: It is also called the ‘Front End’ because that is what the user sees and
interacts with on the computer screen. This tier receives input from the user and
also displays data to the user, either as final output or as a list of valid options
from which the user can make a choice for input.
The common name for this tier is a ‘Form’. Forms can be created either on
worksheets or on special graphical objects called UserForms. In web based
applications, forms are created on webpages.
Second tier: It is often called ‘Middleware’ and it is the actual code that we
write. It acts as a bridge between the other two tiers.
The code takes values from the front end and first validates the values. Then
processes them into values appropriate for storing in databases (third tier). It
then finally stores the processed values in the database.
Middleware also takes reference values from the databases and passes them on
to the front end to display to the user so that the user can select the correst
values. Another function of this tier is to fetch data from the database and
produces reports and transfers output to other applications like word, email and
PowerPoint.
Although called the second tier, in principle, it is the first one to start working
when an application is started. It displays the default form to the user from
where the user can proceed further.
Third tier: It is also called the ‘Back End’. It is the actual storehouse of data of
our application. Although it seems that the back end is mostly inactive, an
effective structure of the database will reduce the complexity of the other two
tiers and also provide enough flexibility to add functionality in the future.
18.1 Introduction
Our goal would be to create an application that will allow users to capture data
on sales transactions through the application’s front end. The user will then be
able to produce various reports. Since our focus is to learn the basics of
development, the application that we will develop has several simplifications as
compared to a real world application having the same functionality. Please bear
with these.
All the three tiers will be created in the same Excel file. If there are multiple
users using separate files, we can find ways to extract and consolidate data from
different files.
We will look at details of designing forms and database in upcoming sections,
but before that we need to clarify some concepts and terminology related to
forms, so let’s do that first.
Information that is stored in a database is almost always captured by filling up
details in a form. Examples are forms that we fill up when buying insurance, or
renting a car or when providing details at a hospital.
There are several components available that make it easy for a user to fill up a
form. These components not only reduce the amount of typing required but also
ensure that only the correct data in the valid format is submitted.
The technical term for these components is ‘Form controls’. We use these
several times on websites and company applications. Here we provide a small
list of controls. We will cover a more comprehensive list later on.
Examples of controls are:
1. Listbox: It presents the user with a list of items. The user can scroll through
the list and select one or more items. Example, the items to be purchased
from a store.

2. Combobox: It presents the user with a drop down list of items. The user can
select one item out of these. Example, the state to which delivery has to be
made.

3. Textbox: Allows the user to type in text. Either in a single line or multiple
lines.
We can create user input forms in two ways by combining controls. We will look
at both the approaches in this part of the book. The figures18.1a and 18.1b
provide a glimpse of forms created with each approach.
1. Creating forms by adding controls directly on a worksheet.

Fig 18.1a A form created on a worksheet

2. Creating forms by adding controls on a Visual Basic userform.


Here is the exact same form created on a Visual Basic userform by adding the
exact same controls.
Fig 18.1b A Visual Basic UserForm

The difference between the two, in terms of the components placed on the form,
is mainly the place where user is allowed to type text. On a spreadsheet we have
to use single cells or merged cells. On a userform we need to use the Textbox
control.
18.2 Scenario for data capture
We want to build an application that will allow an operator to quickly capture
details of sales transactions. User will be able to add one item at a time or
multiple items in one go (conditions apply of course).
Let us look at how the form will be used. In real world scenarios we would have
to get this information from users through discussions. We will ensure that the
user does not have to memorize a large amount of information.

Fig 18.2a Various parts of the front end form

The form will be used as follows (numbers in figure 18.2a correspond to the step
number that we are discussing. Example – step 2 deals with current date and
order ID, both are marked by number 2 in figure 18.2a):
1. A blank order form is presented to the user. The controls contain only their
default values.
2. The current date and latest order ID are automatically filled in. Details of how
the order ID is created is provided in the next section.
3. User now selects, from the dropdown (ComboBox) labelled “Customer”, the
customer for whom the order is being prepared. This list is created from a
range of data present in the workbook.
4. Now the user can add items to the order. There are two options:
a) Adding single item
All available items are listed in a dropdown list (ComboBox) labelled
“Select Item”. User selects one item and enters the quantity in the cell
labelled “Quantity”.

b) Adding multiple items
· User clicks in the checkbox labelled “Add multiple items with standard
quantities”. This enables two ListBoxes. The ListBox on the left contains a
list of all available products. The one on the right is empty.
· User selects one or more items in the ListBox on the left, clicks on “Add”
button. The selected items are added to the ListBox on the right.
· If required, user can remove items from the right side ListBox by selecting
those items and clicking on “Remove”

5. Applying Special discount, if any
· The user clicks on the related checkbox (inside the rectangle labelled
‘Special Discount’). If the box is not checked, any value entered is ignored.
· User selects the value of special discount (%) using the SpinButton.
Default value is ‘0’, Maximum value is 2%. The value is incremented in
steps of ‘0.2%’.
· When the invoice for any is generated, the unit price of each item will be
reduced by the Special Discount applied to that item in that particular order.
· If multiple items are selected, the same discount value is applied to each
item.

6. User clicks on the button labelled “Add Item to List” to save the selected item
and the values and Special Discount to a temporary location along with any
items previously added for the same order.

7. User selects the region in which the sale will be accounted using the
OptionButtons.

8. Applying Cash discount, if any
· The user clicks on the related checkbox (inside the rectangle labelled
‘Order Details’). The CheckBox is labelled ‘Cash Discount’. If this box is
not checked, any value entered is ignored.
· User selects the value of cash discount (%) using a ScrollBar. Default
value is ‘0’, Maximum value is 8.5%.
· When the invoice for any order is generated, the value of the entire invoice
will be reduced by the value of Cash discount applied.
· This discount is in addition to the Special Discount which is applied to an
individual item.

9. Any comments related to the order can be added in the ‘Comments’ section.

10. At any time, the user can cancel the entire order by clicking on the button
labelled ‘Cancel’. All values entered till that point are discarded.

11. When all values have been entered, user can save the entire order by clicking
on the button labelled ‘Save Complete Order’. Two things happen,
A macro will save the details of the entered items and the order (Date, ID,
Region, Cash discount) to a permanent location in the workbook.
The values of controls are restored to their default values. Checkboxes are
unchecked, Discount values are set to ‘0’, ListBoxes are disabled and the
‘Comments’ section is cleared. Order ID is updated for the next order.
18.3 Setting up the database.
Our database will contain two sheets. One which, we will name ‘MasterData’,
will hold all the data that will be referenced either on input forms or by formulas
and macros. The second that we will name SalesDB will hold all the transaction
related data
The MasterData sheet will have two tables as shown in the figures18.3a and
18.3b. One (called ProdDB) holds a list of all the products that we offer. The
other (called RegionList) holds a list of all our sales regions. These tables can be
created on the same worksheet or separate worksheets. For convenience we will
hold them on the same sheet.

Fig 18.3a Table “ProdDB”


Fig 18.3b “RegionList”

We will ensure that we don’t add any spurious data in the rows below these
tables. This is done so that we can easily expand the list when needed and so that
we can use dynamic ranges.
The MasterData sheet will also have one cell that holds the order ID of the last
sale that was made. This is simply the year and month numbers concatenated
with a 5-digit transaction number. We don’t expect over 100000 transanctions in
any given month. For a new order, the last order number is taken and the month,
year and order numbers are increased as per the situation. For each new month,
the first order is given a number ‘000001’.
The SalesDb sheet will contain records of all our sales transactions. The
arrangement of data on this sheet is shown in figure 18.3c. This information is
stored in two separate tables. The table in columns A to D holds data on each
product (denoted by the ProductID) that is included in an order. The table in
columns H to L holds data that is specific to each order (Irrespective of the
number of items included)

Fig 18.3c Tables sto store transaction information on “SalesDB” worksheet

The advantages of having two different tables are as follows:


13. In each order, the customer can include multiple products. So we have one
row for each item included in a particular order. The details are stored in
columns A to D.

14. Each order belongs to only one customer (identified by the CustomerID), is
taken on one date, and belongs to one Sales region. These details are stored in
columns H to L. These details do not vary with the products. So if we were to
store them along with the item details, we would have to repeat this
information in a lot of rows thereby using up a lot of cells.


15. The Special discount is applied to each item separately. The Cash discount is
applied to the total value of the order and is over and above the Special
discount. Having two tables allows us to keep these details separate and
simplifies calculations later on.
Note that the link between the two tables is provided by the OrderID column.
When storing information on orders, we are keeping things simple by not adding
details like shipping or billing addresses or customer details. But data on
customers has to be stored somewhere. We will store this information on the
“MasterData” worksheet in columns M to P as shown in the figure 18.3.d

Fig 18.3d Table to store customer information on “MasterData” worksheet

18.3.1 Setting up named ranges


Now, let’s create some dynamic ranges so that we can use them in our
application. We will create named ranges for the data that we will refer in the
form controls. This data is presented to the user for selection. Here are the
names:


1. The number of rows in our ProductDB
Name: NumProdRows
Formula: =CountA(MasterData!$A:$A)-1

2. The number of columns in ProductDB
For this one, we will just put a constant value. ‘5’ in our case.
But then why create a name?
So that if we use this value in multiple formulas/macros, and in future we end
up adding another column, we have to change the value only at one place.
Name: NumProdCols
Formula: 5

3. The entire ProductDB table
Name: ProdDB
Formula: =Offset(MasterData!$A$1,1,0,NumProdRows,NumProdCols)

4. The list of products
Name: ProdList
Formula: =Index(ProdDB,,2)

5. The cell holding the ID of the last order
Name: LastOrderID
Formula: =MasterData!$J$2

6. Number of rows in RegionList
Name: NumRegionRows
Formula: =CountA(MasterData!$H:$H)-1

7. RegionList itself
Name: RegionList
Formula: =Offset(MasterData!$J$1,1,0,NumRegionRows,1)

8. The number of rows in our CustomerDB (Table starts at column M)
Name: NumCustRows
Formula: =CountA(MasterData!$M:$M)-1
9. The number of columns in CustomerDB
For this one, we will just put a constant value. ‘4’ in our case.
Name: NumCustCols
Formula: 4

10. The entire CustomerDB table
Name: CustomerDB
Formula: =Offset(MasterData!$M$1,1,0,NumCustRows,NumCustCols)

11. The list of customer names
Name: CustList
Formula: =Index(CustomerDB,,2)

18.3.2 Cells for holding data entered in forms


When we create forms by placing controls on a Worksheet, the inputs provided
by users can be held in worksheet cells. From these cells, our macros can read
the data and do the required processing.
For our application, we will be storing data on the same worksheet where we
build our form.
In these cells:
Ø ‘ControlValue’ is the value that is provided by the controls on the form. This
may not be the value we want to store in the Database(DBValue).

Ø ‘DBValue’ is the value that is actually stored in the database tables on
SalesDB worksheet.
The DBValue has to be calculated from the ControlValue using Excel functions
and named ranges. We have shown the cells and related formulas in figures
18.3.2a to 18.3.2c
a) Item specific details entered in controls

Fig 18.3.2a
Values in Columns AY and BA will change each time the user selects a new item
in the form.
When ‘Add Item’ button is clicked, these values will be transferred to a list of
items in the order.
That list is shown in Fig 18.3.2c
b) Order specific details (applicable to entire order)

Fig 18.3.2b
Values in Columns BC and BE will change only when a new order is created.
They will remain same for all items added in the order. When ‘Save Complete
Order’ button is clicked, these values will be transferred to the tables on the
SalesDB worksheet
c) Compiled order having all the items

Fig 18.3.2c List of items in an order

Values in these cells hold a list of all the items that have been added in the order.

Here is how this last range (shown in figure 18.3.2c) is used under different
actions taken by the user:
1. When the ‘Add Item’ button is clicked and a single item is to be added, a
macro will copy the values (not the entire contents with the formulas) from the
range “BA2:BA4” (figure 18.3.2a) and pastes them at the bottom of this range
(columns BG to BI)
When ‘Add Item’ button is clicked and the user has opted for multi item
addition, a macro will read the selected items, and their IDs and will add as
many rows as the number of selected items at the bottom of this range.
2. When ‘Save Complete Order’ button is clicked, a macro will copy all non-
empty the rows in columns BG to BI starting at row 2, and paste them in
columns A to D on SalesDB page (figure 18.3c) in the last non-empty row.
Then the macro will clear all the cells in this range so that a new order can be
created.

3. When ‘Cancel Order’ button is clicked, a macro will clear all the rows in this
range.
Chapter 19
Creating forms on a worksheet
19.1 Setting up the Worksheet
Before we can create a form, we need to develop a canvas on which we can
place the controls. Here is one approach to do this:
1. Create a grid of worksheet cells by giving the certain fixed height and width
to the cells. For our form (shown in figure 18.1a) we have set the width of
columns A to AJ to a value of 3 and, rows 1 to 30 have been given height of
14.
Select the columns. Right click. Select ‘Column Width’. Set the width.
Select the rows. Right click. Select ‘Row Height’. Set the height.

2. Provide a suitable color to the entire sheet or to the portion where form will
be constructed.

3. Put a suitable border around the cells if required

4. Switch off the gridlines.

5. To allow user to type-in text we can put another border around a larger
rectangular group of Cells. We can merge these cells so that the entire group
can be referred to with a single cell address.
19.2 Adding controls
When adding controls to spreadsheet we have two options.
1. Use the Microsoft Excel forms controls
2. Use Microsoft ActiveX controls.
We will use the Excel forms controls. What is the difference and why don’t we
use the ActiveX?
Excel forms controls exist as if they are built into Excel. They are not dependent
on anything outside Excel. This means these controls are always available and
are compatible across versions of Excel.
The ActiveX controls, on the other hand, are built into separate libraries as if
they live outside of Excel. In many organizations, use of these controls may not
be allowed. Also if the libraries holding these controls are not installed on a
user’s computer, the controls may not function.

Some advantages of ActiveX controls:


1. They allow more options for formatting the look and feel of the control.
2. ActiveX controls also have a large range of events to which our macro can
respond. For example, we can write macros that get triggered when a user
presses a mouse button on a control (MouseDown event), and also when he
releases the mouse button (MouseUp event). Or when the user presses a
key (KeyDown) or releases a key (KeyUp).
3. Compared to this, the Excel forms controls allow us to respond to only one
event. Like the ‘Click’ event, which can be seen as a combination of
‘MouseDown’ and ‘MouseUp’ events happening in sequence or, the
‘Change’ event in case of ListBoxes.
4. Advanced programmers can code and use their own customized ActiveX
controls.
5. There are ActiveX controls for much advanced features like playing
animations and movies. But we will not discuss those here.

The fundamental controls used for entering data are the same in the ActiveX
controls group as those available from Microsoft Excel Forms controls. Hence
we will be using the latter. Only disadvantage is that we cannot finely control the
look and feel of the control or write macros to respond to every user action. We
can respond only one user action for any particular control.
For adding a control on a worksheet
1. Go to the ‘Developer’ tab on the ribbon.
2. Click on ‘Insert’ in the ‘Controls’ group. A small menu comes up.
3. In the ‘Form Controls’ group, click on any control. The cursor changes shape
to a ‘cross hair’.
4. Click on the Worksheet at the place where the control has to be placed, and
draw a small rectangle.
5. The control appears along with a small label. Change the text of the label to a
suitable one.
19.3 Setting Properties, Caption and Name of

controls
In this section, we will see how we can set the name and properties of controls
that we add on a Worksheet. Naming a control will allow us to refer to the
control in our macros as if it were a variable.
We will discuss the common properties briefly. For illustration purpose we will
look at the ListBox control. In later chapters when we see individual controls we
will discuss specific properties in detail.
Setting properties:
Select the control.
· Left-click on the control while holding down the ‘Ctrl’ key. OR

· Right click on the control.
Once the control is selected, right click. (not required if control was selected by
right clicking)

In the context menu that comes up, select ‘Format control’

Fig 19.3a Invoking the Format Control dialog box



A dialog box is displayed that has several tabs. The most interesting one for us is
the ‘Control’ tab. The other tabs deal with sizing, positioning and formatting of
the control

Fig 19.3b The Format Control dialog box

The ‘Control’ tab mainly deals with the data that is supplied to and from a
control. It has multiple fields as shown in figure 19.3b. A particular control may
not have all these fields. Some may have few more.

Following points are to be noted:
1. For controls that display multiple items (like ComboBox or ListBox), there is
a field called ‘Input range'. Here we can specify a range of cells holding the
values we want to display. The range can be on the same or separate sheet.
And yes, you are on the right track. We can specify a dynamic named range
for these cells. That way, every time we add a new value to our list, it
automatically appears in our forms for selection.
Warning: We can add named range, not a formula. For example, we cannot
specify “=Index(ProdDB,,2)” when we want a list of products. We should
create a name containing that formula and then add that name in the “Input
Range” field.
2. Another field is the ‘Cell link'. This is the cell that will hold the value that is
selected in the control.
3. For controls that allow selection from a range of values (like ScrollBar and
SpinButton) we may have additional fields for Maximum and Minimum
values.
The ‘Selection Type’ box with entries ‘Single’, ‘Multi’ and ‘Extended’ are
specific to a ListBox control. We will discuss these properties when we discuss
that control in detail.
Setting Caption:
For some controls like Button, CheckBox and OptionButton when we place
them on the screen, Excel provides us a small line of text beside the actual
control.

Fig 19.3c Caption of a Control

This line of text is a part of the control and is called ‘Caption’ of the control as
shown in figure 19.3c.
For the caption, we can write text on the control to guide the user. Here’s how:
· Select the control or Right click on it.
· In the pop-up menu select ‘Edit Text’. Type the text to be displayed.
Caption is not available for controls like ListBox, ComboBox, ScrollBar and
SpinButton.
Setting Name:
When working with controls from our macro, we have to refer to each control by
its name. Names allow us to address the exact control we want without having to
loop through all of them.
Figure19.3d shows how we can set the Name of a ComboBox. The method
remains same for all controls.
· Select the control.
· Click in the ‘Name’ box available beside the ‘Formula’ bar. We can see that
Excel by default provides a name. We can use this name in our code, but we
should put in another name that depicts the purpose of the control
· Type in the desired name
Press ‘Enter’. - Extremely important.
Fig 19.3d Setting name of a control

For naming controls in our application, we will use the initial 2-4 letters of the
name to indicate the type of control (‘cb’ stands for combobox). This will be
followed by a description of what the control offers or does. (‘ProdList’ means a
list of products to choose from). The individual words in the name are separated
by capitalizing the first letter.
These are just guidelines. We can form our own convention for names. We just
need to ensure that the length of the name does not exceed 20-25 characters.
19.4 Link to cells, formulas & macros
Form controls have many aspects that are common for all controls. We will see
specific techniques for each control when we discuss individual controls in
upcoming sections. In this section, we will review the common aspects that are
applicable to all the controls so that we can avoid repetition in upcoming
sections.
Every control placed on a worksheet can be linked to a cell that will hold a value
that is supplied to the control. The value depends on the type of control, but it
can be one of the following types:
1) Numeric integer value:
Linked Cell of Holds
Listbox or a Combobox The position of the selected item as it appears in the
list of values.
A group of option An integer value representing the selected button.
buttons Example, out of four buttons if the third one is
selected, the Linked cell will hold ‘3’.
A SpinButton or An integer value lying in the range specified for the
ScrollBar control.

2) Boolean value
Linked cell of a Checkbox holds a value TRUE if the box is checked, FALSE
otherwise.
The value in the linked cell acts just like a regular cell value. It can be used in
formulas that accept the value of that particular type (integer, true/false).
19.5 Controls & Macros (general facts)
19.5.1 Linking controls to macros
For working with controls that are placed on a worksheet from within our macro,
there are two steps that are initially necessary. These steps are required for any
control we use, only the name of the objects will change. So we will view the
steps here to avoid repetition in later sections.
In later sections, when we see details of a specific control we will skip the
explanation and only look at the syntax for that particular control.
The steps are as follows:
1. Declare an object in Visual Basic for the specific type of control. The
following line of code declares an object that will later on refer to a ListBox
we created on the ‘OrderForm’ worksheet.

Dim lbProdList As ListBox

2. Tie the Visual Basic object to a real control on a Worksheet. For this, we
should note that all the controls placed on a Worksheet are available in
collections of their respective types. All the ListBoxes are held in a collection
called, well, ‘ListBoxes’. The collection itself is a property of the worksheet
and can be accessed with the following syntax:

<Worksheet object>.ListBoxes

A particular ListBox can be accessed as follows:

<Worksheet object>.ListBoxes(strControlName)

Where ’strControlName’ is a string containing the name of the concerned
ListBox.
For example, the code that follows will set the Visual Basic ListBox object to
the ‘lbProdList’ ListBox control we placed on ‘OrderForm’ worksheet.

Dim wks As Worksheet
Set wks = Thisworkbook.Worksheets(“OrderForm”)
Set lbProdList = wks.ListBoxes(“lbProdList”)

And here is a similar code for a ScrollBar control on the same worksheet.
Dim scrlCashDiscount As ScrollBar
SetscrlCashDiscount= wks.ScrollBars(“scrlCashDiscount”)

Warning: The collections like ‘ListBoxes’, ‘DropDowns’and so on are often
not listed by ‘intellisense’ as a property of a worksheet. But these are always
available.
One observation:
Names of most collections (of controls) is the plural form of the control’s name.
All ListBox controls are in ‘ListBoxes’, all ScrollBar controls are in ‘ScrollBars’
collection. Exceptions are the ComboBox and the SpinButton controls. All
ComboBoxes placed on a worksheet are held in a collection called
‘DropDowns’. All SpinButtons placed on a worksheet are held in a collection
called ‘Spinners’. We will revisit this fact when we see ComboBox and
SpinButton controls in detail.
That’s it. Now we can start working with the Visual Basic object in our macro,
and the effects will be reflected on the control placed on our worksheet.

19.5.2 Events
We will see the specifics of controls and macros when we discuss details of each
control. In this section however, we will see the general method of associating a
macro to a control’s event.
An ‘Event’, when talking about controls, can be thought of as a specific macro
that runs when the user does something to the control. Each of the Forms
controls placed on a worksheet, allow us to create one event macro. The
following table lists when the macro associated with each event would get
triggered:
Control(s) Event Name Macro is executed when
ListBox, Change User changes the value of the control either by
Combobox, selecting (ListBox/Combobox) or by changing
ScrollBar, the value (SpinButton/ScrollBar).
SpinButton
CheckBox, Click User clicks on the control. The value
Optionbutton, automatically changes from ‘True’ to ‘False’
or the other way depending on the value it was
holding when user clicked.
Button Click User clicks on the control.

To respond to an event, we have two options (refer to figures 19.5.2a and
19.5.2b):
1. Assign a blank default macro and write our code in it.
· Right click on the control and select ‘Assign Macro’ in the pop-up menu.
· In the ‘Assign Macro’ dialog box that is shown, click the “New” button.
· Excel will generate an empty Sub procedure. We can type in our code
into this sub-procedure
2. Create our own sub procedure and assign it to the control
· First create a sub-procedure (the name need not contain ‘Click’ or
‘Change’)
· Right click on the control and select ‘Assign Macro’ in the pop-up menu.
· In the Assign Macro dialog, select the sub-procedure just created and
click ’OK’
Important note: The new value of the control is available for use inside the
event’s macro. It’s as if the value is set and then the event macro’s code is
executed.
For example, when the user clicks on a previously unchecked checkbox, the
value is set to TRUE and then the macro executes. Within the macro if we check
the value of the control, we get a TRUE value.


Fig 19.5.2a Invoking the Assign Macro dialog

Fig 19.5.2b The Assign Macro dialog

19.5.3 Common properties


The objects we create in our macro to refer to controls placed on a worksheet
will have several properties that we can read and set from within our macro.
Few of these properties are available in all the controls. We will discuss them
here in order to avoid repetition in later sections. When we reach a specific
control, we will just look at the syntax for that specific control.
Here are those properties:
1. LinkedCell: This property holds a string value containing the address of the
cell that holds the value of the checkbox. It is set via the ‘Cell link’ field in
the ‘Fromat Control’ dialog box. The syntax for using this property is as
follows:

<Control_obj>.LinkedCell

“Control_obj” is the Visual Basic object we created using the ‘Dim’
statement.
In the code that follows, ’scrlCashDiscount’ is a Visual Basic ScrollBar object
linked to a ScrollBar control placed on a worksheet. It is declared and
initialized with the following code:
Dim scrlCashDiscount As ScrollBar
SetscrlCashDiscount= wks.ScrollBars(“scrlCashDiscount”)


The following code snippets show the usage of the LinkedCell property for a
ScrollBar control.
a) Printing the current value

Debug.Print scrlCashDiscount.LinkedCell

b) Setting to a cell on the same or different worksheet

scrlCashDiscount.LinkedCell = “MasterData!$AK$1”

c) Setting to a named range (name can refer to a cell or a formula that returns
a single cell)
scrlCashDiscount.LinkedCell = “DummyLinkCell”

The Name ’DummyLinkCell’ is name of a range. The name could refer to


a formula like
=Offset(SalesDB!$I$2,1,1)

However, the formula should return only a single cell.


Although we can set the ‘LinkedCell’ property in macros, it is advisable to set
the property in the ‘Format Control’ dialog box when we are designing the
form and then only read this property when working in a macro.
2. Visible: This property has a Boolean value and allows us to show or hide the
control on the screen using our macro code. If the value of this property is set
to TRUE, the related control is visible. A value of FALSE hides the control
from view and the user is not able to interact with it.

Continuing with our example of a ScrollBar control, the syntax to hide that
control would be:
scrlCashDiscount.Visible = False

The syntax to show it again would be:


scrlCashDiscount.Visible = True

By default, a control is always visible. So in case we want to hide the control


when the workbook is first opened, we should set the control’s ‘Visible’
property to ‘False’ in the workbook’s ‘OnOpen’ event which is available in
the ‘Workbook_Open’ sub procedure.

3. Enabled: This property has a Boolean value and allows us to enable or
disable a control using our macro.
When a control is disabled, it is visible on the screen, but the user won’t be
able to interact with the control. So it will not generate any event.
For our ScrollBar control, the syntax to disable that control would be:

scrlCashDiscount.Enabled = False

The syntax to enable it again would be:

scrlCashDiscount.Enabled = True


Important note:
A disabled control is a bit confusing because it appears on the worksheet as a
normal control. But when the user clicks on it nothing happens. This might
cause a confusion for the user. If we want to prevent input in a control, it is
better to set the visible property to ‘False’.
4. Caption: This property has a string value and allows us to read and set the
caption of the control. (Note: This property is available only for Button,
OptionButton and CheckBox)
For our OptionButton control, the syntax to print the caption is:
Debug.Print opBtnNorth.Caption

The syntax to set a new value for the caption is:opBtnNorth.Caption = “Please select a
value”

Here ’opBtnNorth’ is an OptionButton object that refers to a OptionButton


named ‘opBtnNortn’ placed on a worksheet.
19.6 ListBox Control
Listbox is a control that presents a list of items to the user. User can scroll
through the list and can select one or more items (depending on the setting of the
control).
To place a ListBox on a worksheet, first open the forms controls pallet by going
to ‘Developer’ tab on the main ribbon and selecting ‘Insert’.

Fig 19.6a

Listbox control can be placed on a worksheet by clicking on the icon marked in
the figure 19.6a
When placing the control, it’s better to draw a rectangle high enough so that at
least 8-10 lines of items are visible.

In section 19.3, we saw how we can specify the name of a control. Now let’s see
how we can set the essential properties of a ListBox control.
· Select the control.
· Right click and select ‘Format Control’ from the popup menu.
· ‘Format Control’ dialog box is displayed as shown in the figure 19.6b.

Fig 19.6b Format Control dialog box



The following table shows the meaning of different properties.
The ‘Selection type’ can be one of the following:
Selection type Features
Single User can select only one value from the list.
Extend User can select the first item by clicking on it.
After that, if user clicks on any other item, that item
becomes the selected item, the first item is discarded.
To select additional items, the user has to hold down the
‘Ctrl’ key and make subsequent selections.
To deselect an item, the user has to hold down the ‘Ctrl’
key click on that item again.
Multi User can select the first item by clicking on it.
After that, if user clicks on any other item, that item also
gets selected
To deselect an item, the user has to click on that item
again.

It seems that ‘Multi’ is the one option where a user can make multiple selections
without the extra effort of searching for and holding down the ‘Ctrl’ key. Hence
we prefer ‘Multi’.

Linking to cells/formulas on a worksheet


The figure 19.6c shows a ListBox on a worksheet named ‘Sheet4’ (shown on the
left) with ‘SelectionType’ set to ‘Single’ and ‘Input range’ set to column B on a
worksheet named ‘MasterData’ (shown on the right)
Fig 19.6c Linking a ListBox on one sheet to Range on another sheet

The figure 19.6d shows the properties of the ListBox on Sheet4. Since the
ListBox does not provide the displayed value but the position of the value in the
list, we have to use one of the functions from chapter 7 to fetch the required
value from the input range.

Fig 19.6d Properties of a ListBox control

Referring to figure 19.6c, cell B4 on Sheet4 contains the formula with ‘Index’
function. The ‘RowNum’ argument of ‘Index’ function is equal to the value
returned by the list box. The Index function fetches the ‘ProductID’ of the
selected product.

Properties & Methods in Macros


In section 19.5.1 we have seen how we can link a control placed on a worksheet
with an object in our macro’s code. Here is the code for a ListBox.
Dim lbProdList As ListBox
Set lbProdList = wks.ListBoxes(“lbProdList”)

Now let’s see the important properties and methods of this control.

Properties
The properties listed in the table below are common to all the controls and have
been described in detail in section 19.5.3. We will just see specific syntax for a
ListBox here:
LinkedCell lbProdList.LinkedCell = “MasterData!$AK$1”
Visible lbProdList.Visible = False
Enabled lbProdList.Enabled = True

Now we will see some other important properties specific to a ListBox.
1. List: This property holds an array of all the items in the ListBox. The
datatype of the returned array is Variant.

Each item in the list has an index position. The first item has the index
position of ‘1’. The item at the position ‘i’ is given by:

lbProdList.List(i)

If a multicolumn range was specified in the ‘Input range’ property, then only
the first column is returned as an array in the ‘List’ property.

The code below loops through and prints all the items in a ListBox. Notice
how we have avoided the problem of not knowing lower bound of the returned
array.
Dim startPos, endPos as Long
startPos = LBound(lbProdList.List)
endPos = LBound(lbProdList.List)
For i = startPos to endPos step 1

Debug.Print lbProdList.List(i)
Next i


The values held in the array are actually Strings (if the entries are
alphanumeric) or Double or Integers based on the values held in the range that
we provide in the ‘Input range’ property.

For example, if we specify the range shown the adjoining figure 19.6e, we will
not be able to perform mathematical operations on all the values because the 3rd
and 7th values are strings.
Fig 19.6e Sample values in a range

Hence we need to be careful that the range we specify has values of the
same datatype in all the cells. These should match the operations we want
to perform on the values in our macro.

2. ListCount: This property returns a Long integer value showing the total
number of items available in the ListBox. We can only read this property.
Syntax is as follows:

lbProdList.ListCount

3. ListFillRange: This property holds a string value containing the address or
Name of the cell range specified in the ‘Input range’ field in the ‘Fromat
Control’ dialog box. The following code snippets show the usage of this
property.

a) Printing the current value

Debug.Print lbProdList.ListFillRange

b) Setting to a range on the same or different worksheet

lbSelList.ListFillRange = "MasterData!$B$2:$B$9"

c) Setting to a named range (name should refer to a cell or a formula that
returns a single cell)

lbProdList.ListFillRange = “ProdList”

The Name ’ProdList’ refers to a formula

“=INDEX(ProdDB,,2)”

The Name ’ProdDB’ and ’ProdList’ are named ranges we created in
section 18.3.1.
Although we can set this property in macros, it is advisable to set the
property in the ‘Format Control’ dialog box when we are designing the
form and then only read this property when working in a macro.

4. Selected: This property holds an array of Boolean values. The length of the
array is the same as the array returned by the ‘List’ property.

The ‘i’th item in the array is given by

lbProdList.Selected(i)

The value of the item is TRUE if the item has been selected by the user, else it
is false.
In a single selection ListBox only one item will be TRUE, while in a multi
selection ListBox, multiple items will have a TRUE value.
The following code loops through all the values in the ListBox and prints only
the selected values.

Dim startPos, endPos as Long
startPos = LBound(lbProdList.List)
endPos = LBound(lbProdList.List)
For i = startPos to endPos step 1
If (lbProdList.Selected(i)) Then
Debug.Print lbProdList.List(i)
End If
Next i


5. Value: This property returns a Variant datatype that holds the value of a
single selection ListBox. This property cannot be used when the selection
type of the ListBox is set to multi/extended types.

‘Value’ in this case does not refer to what is displayed in the ListBox but the
position of the item that the user selected in the ListBox. The code below
prints out the current value of the ListBox

Debug.Print lbProdList.Value

But what if we want to get the value displayed in the list and not just the
position?
We combine the ‘List’ and ‘Value’ properties. Here is the code to print the
selected text that appears in ListBox.

Debug.Print lbProdList.List(lbProdList.Value)

Methods
1. AddItem: This method is used to add items in a ListBox whose ‘Input range’
property has not been set. The syntax is as follows:

lbProdList.AddItem(item, position)


Arguments:
a) Item: this should be a string or number. It specifies the value to be added
in the ListBox.
b) Position: this is an integer specifying the position where the new item has
to be added. This is an optional argument, omitting it would add the item
in the bottommost position in the list.
We should ensure that the input range is not specified for the ListBox for
which this method is being called. Otherwise, the original list is first cleared
and then the new item is added to the list.
2. RemoveAllItems: This method deletes all the items from a ListBox. If an
Input range has been specified, that value is cleared without reporting an
error. Hence we should make sure we don’t invoke this method on a ListBox
for which an Input range has been specified using the ‘Format Control’ dialog
box.

lbProdList.RemoveAllItems

3. RemoveItem: This method allows us to remove an item from a particular
location in a list box for which the Input range is not specified. The syntax is

lbProdList.RemoveItem(index)

We should ensure that the input range is not specified for the ListBox for
which this method is being called or else the macro displays an error and
hangs.
Events
1. Change: This event gets triggered as follows:

a) Selection Type is set to ‘Single’: In this case the event occurs whenever the
user selects an item hence causing the ‘Value’ property to change.

b) Selection Type is set to ‘Multi’ or ‘Extend’: Event occurs whenever the user
clicks in the ListBox to either select or deselect an item.

19.7 ComboBox
ComboBox is a control that presents a list of items to the user in a dropdown list.
User can scroll through the list and can select only one item. ComboBox control
can be placed on a worksheet by clicking on the icon marked in the following
figure.

Placing a combobox.

Next we will set the essential properties and name of a combobox. Refer figure
19.7a.
A combobox returns a value that is equal to the position of the selected item in
the list. The first item has a value of ‘1’. This value is held in the cell specified in
the ‘Cell link’ property.

Fig 19.7a Properties for combobox labelled “Customer”.



User can select one customer from those listed in the named range “CustList”.
Cell BC5 will hold the position of the selected value.

Heads up:
The term ComboBox is actually a misnomer. Real ComboBox is one where a
user can select from a dropdown list but if required, the user can type in the
desired text. The Excel form control we are discussing just allows the user to
select a value from a dropdown list.
The actual term should be ‘DropDown’. However, ComboBox is the term that
Excel came up with, so we will use that. However, things will change when we
move on to working with the control in macros.

Linking to cells/formulas on a worksheet
We can use the position of the selected item returned by a combobox in Excel
formulas to retrieve other information about the selected item from a range using
Offset or Index functions.
For an example from our application, see fig 19.7b. Here the value returned by
the “Customer” ComboBox is used as the ‘Row number’ argument of the ‘Index’
function.

Fig19.7b

We have used the position of the selected customer to retrieve the customer’s ID
from the ‘CustomerDB’.
We will store the ID in the sales tables with the other order details and not the
customer name. The formula for retrieving the customer ID is in cell BE5.

Properties & Methods in Macros


In section 19.5.1 we have seen how we can link a control placed on a worksheet
with an object in our macro’s code. Here is the code for a ComboBox
Dim cbCustList As DropDown
Set cbCustList = wks.DropDowns(“cbCustList”)

Now let’s see the important properties and methods of this control.
Properties
The properties listed in the table below are common to all the controls and have
been described in detail in section 19.5.3. We will just see specific syntax for a
ComboBox here:
LinkedCell cbCustList.LinkedCell = “MasterData!$AK$1”
Visible cbCustList.Visible = False
Enabled cbCustList.Enabled = True

Now we will see some other important properties specific to a ComboBox.
1. List: This property holds an array of all the items in the ComboBox. The
datatype of the returned array is Variant. Each item in the array has an index
position. The first item has the index position of ‘1’.
The item at the position ‘i’ is given by:

cbCustList.List(i)

The code below loops through and prints all the items in a ComboBox. Notice
how we have avoided the problem of not knowing lower bound of the
returned array.
Dim startPos, endPos as Long
startPos = LBound(cbCustList.List)
endPos = LBound(cbCustList.List)
For i = startPos to endPos step 1

Debug.Print cbCustList.List(i)
Next i


The values held in the array are actually Strings (if the entries are alphanumeric)
or Double or Integers based on the values held in the range that we provide in
the ‘Input range’ property.

For example, if we specify the range shown in figure 19.7c. Then we will be able
to perform mathematical oparations on all the values except for the 3rd and 7th
values.

Fig19.7c

Hence, we need to be careful that the range we specify has only values of the
same datatype. These should match the operations we want to perform on the
values in our macro.

2. ListCount: This property returns a Long integer value showing the total
number items available in the ComboBox. We can only read this property.
Syntax is as follows:

cbCustList.ListCount

3. ListFillRange: This property holds a string value containing the address or
Name of the cell range specified in the ‘Input range’ field in the ‘Fromat
Control’ dialog box. The following code snippets show the usage of this
property.

a) Printing the current value

Debug.Print cbCustList.ListFillRange

b) Setting to a range on the same or different worksheet

cbCustList.ListFillRange = "MasterData!$N$2:$N$9"

c) Setting to a named range (name can refer to a cell or a formula that returns
a single cell)

cbCustList.ListFillRange = “CustList”

The Name ’CustList’ refers to a formula

“=INDEX(CustomerDB,,2)”

The Name ’CustomerDB’ and ’CustList’ are named ranges we created in
section 18.3.1.
Although we can set this property in macros, it is advisable to set the
property in the ‘Format Control’ dialog box when we are designing the
form and then only read this property when working in a macro.

4. Value: This property returns a Variant datatype that holds the value of the
ComboBox.

‘Value’ in this case does not refer to what is displayed in the ComboBox but
the position of the item that the user selected in the ComboBox.The code
below prints out the current value of the ComboBox

Debug.Print cbCustList.Value

Methods
1. AddItem: This method is used to add items in a ComboBox whose Input
range property has not been set from the ‘Format Control’ dialog box. The
syntax is as follows:

cbCustList.AddItem(item, position)

Arguments:
a) Item: this should be a string or number. It specifies the value to be added
in the ComboBox.
b) Position: this is an integer specifying the position where the new item has
to be added. This is an optional argument, omitting it would add the item
in the bottommost position in the list.
We should ensure that the input range is not specified for the ComboBox
for which this method is being called. Otherwise, the original list is first
cleared and then the new item is added to the list.
2. RemoveAllItems: This method deletes all the items from a ComboBox. If an
Input range has been specified, it is cleared without reporting an error. Hence
we should make sure we don’t invoke this method on a ComboBox for which
an Input range has been specified.

cbCustList.RemoveAllItems

3. RemoveItem: This method allows us to remove an item from a particular
location in a ComboBox for which the Input range is not specified. The
syntax is

cbCustList.RemoveItem(index)

We should ensure that the input range is not specified for the ComboBox for
which this method is being called or else the macro displays an error and
hangs up.

Events
1. Change: This is the only event available for this control. It gets triggered as
when the user selects a new value in the ComboBox hence updating the Value
of the control.

19.8 CheckBox
Checkbox is a control that provides a choice for one specific item. The user can
put a check mark in a checkbox if the item is to be selected. A Checkbox control
can be placed on a worksheet by clicking on the icon marked in the following
figure.

If the user puts a check mark in the box by clicking on it, the checkbox control
holds a value of ‘True’ in the linked cell. If the box is unchecked, the control’s
value in the linked cell is ‘False’
Next we will set the essential properties and name of a checkbox. Refer figure
19.8a.

Fig 19.8a Properties of a checkbox

Name of the control can be set using method shown in section 19.3
As per figure 19.8a, value of the checkbox will be held in cell A7. After setting
the properties, the box will be initially unchecked.
The ‘Value’ property can be set to one of the following:
Checked A ‘tick’ mark appears in the CheckBox control. Value of the
control is set to ‘True’ that appears in the linked cell
UnChecked A ‘tick’ mark does not appear in the CheckBox control. Value of
the control is set to ‘False’ that appears in the linked cell
Mixed Checkbox is greyed and no tick mark appears. The value in the
linked cell is ‘#N/A’.
This value represents a state between checked and unchecked or,
a NULL state. It appears only initially after setting the property.
Once the checkbox is in use, the value will be either ‘checked’
or ‘unchecked’. It can be set again only from a macro.
If the value of the checkbox held in the linked cell is used in
formulas, then we should avoid this state.

Linking to cells/formulas on a worksheet
We can use the value returned by a CheckBox in Excel formulas and functions
that accept a Boolean value. For an example from our application, see fig 19.8b.
Value returned by the CheckBox labelled ‘Cash Discount’ is stored in cell BC2.

Fig 19.8b Checkbox value linked to a cell

If user checks the box, we will apply the cash discount whose value is in cell
AC14. If the user has not checked the box, we will ignore that value.
Properties and Methods in Macros
In section 19.5.1 we have seen how we can link a control placed on a worksheet
with an object in our macro’s code. Here is the code for a ComboBox.
Dim chkCashDiscount As CheckBox

Set chkCashDiscount = wks.CheckBoxes(“chkCashDiscount”)

Now let’s see the important properties and methods of this control.
Properties
The properties listed in the table below are common to all the controls and have
been described in detail in section 19.3. We will just see specific syntax for a
CheckBox here:

LinkedCell chkCashDiscount.LinkedCell = “MasterData!$AK$1”
Visible chkCashDiscount.Visible = False
Enabled chkCashDiscount.Enabled = True

Now we will see some other important properties specific to a CheckBox.
1. Value: This property returns a Variant datatype that holds the value of the
CheckBox.
Important note: this value is not the same as that held in the linked cell. The
linked cell will hold a Boolean value, while in macro the value is of Integer
datatype. Following table shows the values and their effects:
Value Linked cell Represents
contains
Below 0 FALSE ‘UnChecked’ state. No tick mark appears in the
(-4146) checkbox. Box is empty.
1 TRUE ‘Checked’ state. A tick mark appears in the
checkbox.
2 #N/A ‘Mixed’ State. Checkbox is greyed out.

The code below prints out the current value of the CheckBox

Debug.Print chkCashDiscount.Value

In macros, the most common task is to execute some code based on value of the
checkbox. We can either check the value directly from the control or check it
from the linked cell on the worksheet. The table below shows the code for both
these techniques:
Checking Value property Checking Value in the linked cell on a
of the checkbox directly worksheet
Dim chVal as Variant Dim chVal as Variant
Dim wks, sAdr As String
Chkval = _ wks = _
Split(chkCashDiscount.LinkedCell,”!”)(0)
chkCashDiscount.Value
sAdr = _ Split(chkCashDiscount.LinkedCell,”!”)(1)
chval = _
Worksheets(wks).Range(sAdr).Value
‘//** Checkbox is checked **// ‘//** Checkbox is checked **//
If (chkval=1) Then If (chkval) Then
End If End If
‘//** Checkbox is unchecked **// ‘//** Checkbox is unchecked **//
If (chkval<0) Then If Not(chkval) Then
End If End If
‘//** Checkbox is greyed **// ‘//** Checkbox is greyed **//
If (chkval = 2) Then If IsError(chkval) Then
End If End If

Methods
This control does not have any method of much practical use. Available methods
deal with positioning of the control on the screen. We will not explore them as it
is better not to move the controls with a macro once a form is designed.
Events
1. Click: This is the only event available for this control. This event gets
triggered whenever the user clicks on the checkbox. A click on the checkbox
causes the ‘Value’ property to toggle.

The macro to run in response to this event can be created as discussed in
section 19.5

19.9 OptionButton
OptionButton is a control that allows the user to select one item out of a fixed
list of items. If user selects any one item, the others are automatically deselected.
An OptionButton control can be placed on a worksheet by clicking on the icon
marked (on the ‘Forms Controls’ pallet) in the following.

Since more than one items have to be presented, OptionButtons are always
placed in groups of two or more. A user can select an option by clicking on the
corresponding OptionButton.
Before we proceed we need to get familiar with one term that we will need going
forward.

Option Group:
An option group is a list of items, presented to the user as a group of
OptionButtons, from which the user can select one item.
Other items in the group get automatically deselected.

Name of the control can be set using method shown in section 19.3. However,
we should set a different name for each individual OptionButton. We cannot set
one name for all the buttons in the entire group.

Fig 19.9a

For example, on our form we have an option group for the Sales Region shown
in figure 19.9a. Each of the buttons is given a unique name like ‘opbtnNorth’,
‘opbtnSouth’ and so on representing one sales region.
Next we will set the essential properties of a checkbox. Refer figure 19.9b. The
only property to be set is the ‘Cell Link’. However, when we set this property for
one OptionButton in an option group, the other OptionButtons in the same group
automatically gain this value.

Fig 19.9b Properties of an option button

Important:
Value stored in cell referred to by ‘Cell Link’ is the value for the entire option
group. This value is an integer representing the option button that is currently
selected.

19.9.1 Proper way of putting option buttons


When we put optionbuttons on a worksheet one by one they all become part of
the same option group. But what if we want two different optiongroups on the
same form?

Fig 19.9.1a Two different option groups

For example, when collecting data on a group of students we might have two
option groups as shown in Fig 19.9.1a
1. For the level of education
(cell link set to B28)

2. Gender (cell link set to C28)
Trying to forcefully change the ‘Cell link’ property produces erroneous results.
This is where the ‘GroupBox’ control comes in.
Apart from making our form visually appealing, the GroupBox control performs
an important function of separating the OptionGroups. All OptionButtons placed
inside one GroupBox become part of the same OptionGroup

Fig 19.9.1b

A GroupBox can be placed on the form by clicking on the icon shown in figure
19.9.1b.
This control doesn’t have any properties. We can only set its caption. For
example, ‘Group’ or ‘Gender’ in figure19.9.1a. Setting its name is not of much
use

Here are the general principles to be followed when creating option groups on
forms:
1. As far as possible, decide the option groups and all the options within the
group before placing the controls.
Remember, option groups are effective if we want to present upto 5 items to
the user, an option group is not recommended for a very long list.
Above 5 items, an option group takes up lot of space and ruins the visual
appeal of the form. In case of longer lists, it is better to use a ComboBox.

2. For every option group, first place the GroupBox and then place all the
option buttons within that group one at a time. Do not copy the previously
placed buttons. This may seem like lot of work, but it will avoid problems
later on.

3. Set the ‘Cell link’ for any one OptionButton in a particular option group and
check that all the buttons in the same group have the same value for this
property.

4. Set the name and caption for each individual OptionButton.

5. Move on to creating the next option group.
Linking to cells on a worksheet
Since the value of a group of OptionButtons is an integer, we can use it in
formulas that require an integer.
Let’s see how we can couple an option group with the ‘VLookup’ function. The
principle can be extended to other functions.

Fig 19.9c Using value of a group of OptionButtons in Excel Formulas

Consider the figure 19.9c


Table on the right is a typical engineering data table. Each item is in its own row.
The columns (AH to AJ) hold the specifications of each item. An item’s
description is in column AG. Last column is the maximum load that is allowed
on the item.
Specifications for the item depend on the Pipe Size in the first column.
Cell link property of the option group (Select Detail) is set to cell AD2 as shown
by the thick blue colored arrow.
The user enters a pipe size in cell AB2 and, in the option group, selects the detail
he/she wants to see.
The yellow colored cell AB5 contains a VLookup function whose
“column_index_number’ argument is derived from the option group.
Why “AD2+1” in the formula?
The lowest value of the option group is 1, while the columns we want to retrieve
start at column 2 in the table. The first column has the lookup values.
Properties & Methods in Macros
In section 19.5.1 we have seen how we can link a control placed on a worksheet
with an object in our macro’s code. Here is the code for a OptionButton.
Dim optbtnDesc As OptionButton
Set optbtnDesc = wks.OptionButtons(“optbtnDesc”)

Now let’s see the important properties and methods of this control.
The properties listed in the table below are common to all the controls and have
been described in detail in section 19.5.3. We will just see specific syntax for an
OptionButton here:

LinkedCell optbtnDesc.LinkedCell = “MasterData!$AD$2”
Visible optbtnDesc.Visible = False
Enabled optbtnDesc.Enabled = True

Now we will see some other important properties specific to an OptionButton
1. Value: This property returns a Variant datatype that holds the value of the
OptionBtton.
Important note: this value is not the same as that held in the linked cell. The
linked cell will hold an Integer value for the entire option group, while in macro
the value is of Integer datatype for an individual button. Following table shows
the values and their effects:

Value Linked cell Represents
contains
Below 0 Integer Button is not selected. Some other option in the
(-4146) group is selected.
1 Integer Button is selected. None of the other options in the
group are selected.

The code below prints out the current value of the CheckBox

Debug.Print optbtnNorth.Value

In macros, the most common task is to execute some code based on the options
that is selected. We can check the value either directly from the control (integer)
or from the linked cell on the worksheet. In either case our macro has to check
the entire option group and decide what to do.
The best way to achieve this is to use our trusted ‘Select-Case’ block. Here is the
code. We will use the option group shown in figure 19.9c
We are just displaying the option selected, but of-course we can take more
complex actions.
‘wks’ is the Worksheet object set to the Sheet holding the controls in figure19.9c

Checking value of Linked Cell:

Select Case wks.Range("AD2").Value
Case 1:
MsgBox "So you want to show Description, eh?"
Case 2:
MsgBox "So you want to show Spacing, eh?"
Case 3:
MsgBox "So you want to show Nut/Bolt size, eh?"
Case 4:
MsgBox "So you want to show Max. Load, eh?"
End Select


Checking value of the control:
The OptionButtons on the worksheet have been given names. They are self-
explanatory from the code.
Notice the little trickery used in the Select-Case block.
Usually we take a variable whose value can change and compare it with a
constant in each ‘Case’ statement. Like in the previous piece of code we
compared cell values with constants 1,2,3 or 4.

Here we have taken a constant (1) and in the ‘Case’ statements, we are holding
the variables (Values of OptionButtons. These keep changing)

Select Case 1
Case wks.OptionButtons("opbtnDesc").Value:
MsgBox "So you want to show Description?"
Case wks.OptionButtons("opbtnSpacing").Value:
MsgBox "So you want to show Spacing?"
Case wks.OptionButtons("opbtnNutBolt").Value:
MsgBox "So you want to show Nut/Bolt size?"
Case wks.OptionButtons("opbtnMaxLoad").Value:
MsgBox "So you want to show Max. Load?"
End Select


19.10Button
Button is a control that provides a way to start an action (via a macro). User can
click on a button and this action can trigger a macro. Button control can be
placed on a worksheet by clicking on the icon marked on the Forms Control
palette shown in the following figure.

Name of the control can be set using method shown in section 19.3.
The ‘Format control’ dialog box allows us to set to the font of the text that
appears on the button. We can use this feature to make the button more visually
appealing.
The basic function of a Button is to activate a macro. For this we have to
associate a Sub procedure (functions cannot be associated) with a button. The
procedure to associate a macro to a button or any control is discussed in detail in
section 19.5.2 “Events”.
19.11 ScrollBar
Scroll is a control that provides a way to select one numeric value from a range
of values. ScrollBar control can be placed on a worksheet by clicking on the icon
marked in the figure shown on the left.

Name of the control can be set using method shown in section 19.3
Fig 19.11a shows the terms associated with different parts of a scrollbar control.
We need these terms for setting the properties of a scrollbar.
ScrollArrows: These are arrows at each end of the scrollbar. Clicking on any of
the arrows once will cause the value property to change by a fixed amount.
ScrollBox: The part that the user can drag towards one of the ends of the
scrollbar to change the scrollbar’s value.
Value can also be changed by:
· Clicking on one of the Scroll arrows.
· Clicking between the Scroll box and one of the scroll arrows

Fig 19.11a A Vertical & Horizontal ScrollBars and their parts

Next we will set the properties of a ScrollBar. Refer figure 19.11b.


Fig 19.11b Properties of ScrollBar

Important Note:
Referring to figure 19.11b. All these values are positive integers. We cannot
have fractional values or negative values. We will not be setting the ‘Current
Value’ property. We can leave it at default and let it vary as the control is used.


a) Maximum Value: This is the value the control will have when the scroll box
is at the ‘Point of Max value’ as marked in figure 19.11a.
This value has to be greater than the Minimum value. Largest value allowed
is 30000.

b) Minimum Value: This is the value the control will have when the scroll box
is at the ‘Point of Min value’ as marked in figure 19.11a.
This value has to be less than the maximum value. Least value allowed is 0.

c) Incremental change: The amount by which value of the control will change
when the user clicks once on one of the scroll arrows.
It should be noted that the increment does not dictate the value of the control.
For example, for the settings shown in figure 19.11b, it is possible for the
control to take a value of ‘190’ based on position of the scroll box.

d) Page change: The amount by which value of the control will change when
the user clicks once between one of the scroll arrows and the scroll box.

e) Cell link: Address of the worksheet cell that will hold the control’s value.
19.11.1 Fractional/Negative values & reversing the
direction
In case of a scrollbar there is only one cell that we can link to the control in a
straightforward manner.
In this section we will discuss how to tackle two important issues we face when
using a scrollbar. And for understanding the solution, we need a new concept
that we will call a ‘Target Cell’
A Target cell is a cell different from a linked cell and will hold a value derived
from the linked cell. Our formulas and macros will use the value in the target
cell instead of that in the linked cell.
1. Getting fractional values from a ScrollBar
Suppose in our application, we decide to give a cash discount on an order and
we want it to be at least 0.5% but not exceed of 8.5%. Also, user should be
able to increase it by 0.25% by clicking on the scroll arrows.

How do we achieve that with a scrollbar control that provides only integer
values?
For obtaining decimal values, we need to convert each of the decimal values
to fractions of the form (a/b) ‘a over b’. Where ‘a’ and ‘b’ are positive
integers.
The catch is that value of ‘b’ should be same for Minimum, Maximum and
Increment properties and such that all these three properties are positive
intergers.
For our example, let us take different values of b and work out the fractions
for Minimum, Maximum and Increment properties.
Value of Minimum Maximum Incremental
‘b’
(should be (should be change
equal to 0.5) equal to 8.5) (should be
equal to 0.25)
10 5/10 85/10 2.5/10
100 50/100 850/100 25/100

We notice that taking b = 10, the value of ‘a’ or numerator for the ‘Incremental
Change’ is still a decimal number. To convert that to integer we had to take
b=100.
Now that the value of b is finalized, here is how we proceed.
1. Set the values of Minimum, Maximum and ‘Incremental Change’ properties
to their respective integer values.
2. In the target cell we enter the formula “= <linked cell>/ b”

Fig 19.11.1a Settings for fractional value

For our example, the values in the ‘Format Object’ dialog box are shown in
figure 19.11.1a
The Target Cell will have the formula
= $AV$2/100
Now when we move the ScrollBox, the ‘Target cell' will hold the desired
decimal value, that lies between 0.5 and 8.5
2. Inverting the max and min positions of a ScrollBar
Suppose we want.to show a vertical scroll bar that acts a means to vary
temperature or water level.

Notice how we bring in our goal of user-friendliness. We make the controls


more intuitive

Obviously, for making it intuitive for the user, we want that when the user
moves the scroll box up, the value shown should increase. However, we know
that for a vertical scrollbar, the value falls as we move towards the top arrow.
We proceed as follows.
1. Let us denote the desired maximum and minimum values be maxval and
minval so that we don’t confuse them with the Maximum and Minimum
properties.
2. Work out the difference between maxval and minval. Let us call this value
‘diff' for difference.
3. Set the Minimum property of the scrollbar to 0 and the Maximum property
to ‘diff’.
4. In the target cell, enter the formula
“=maxval-<linked cell value>”
Now when we move the scroll bar towards the top arrow, the value in the
target cell will move towards the maxval.

Fig 19.11.1b Settings for inverting the Max and Min values

The values in the ‘Format Control’ dialog box shown in figure 19.11.1b are for
obtaining a value of 10000 at the top of the scrollbar and value of 100 at the
bottom
The Target Cell will have the formula
= 10000 - $I$41
2. Negative values
Dealing with negative values when both, the Maximum and Minimum
property values are negative is straightforward.
We set the Maximum and Minimum property values to the respective positive
numbers and then in the target cell we enter the formula

“=-*<linked cell value>”.
Here we will discuss a more general situation where the value of Minimum
property has to be negative and that for Maximum property has to be positive.
We proceed as follows.
1. Let us denote the desired maximum and minimum values be maxval and
minval so that we don’t confuse them with the Maximum and Minimum
properties.
Let us take a specific example where maxval is 100 and minval is (-30)
2. Work out the difference between maxval and minval. Let us call this value
‘diff' for difference. Note that to subtract a negative value from a positive
one we have to add the two positive values.
Diff = maxval – minval = 100 – (-30) = 130
3. Set the Minimum property of the scrollbar to 0 and the Maximum property
to ‘diff’.
4. In the target cell, enter the formula
“=maxval-<linked cell value>”
Now when we move the scroll bar towards the top arrow, the value in the
target cell will move towards the maxval, 100 in our example.

Fig 19.11.1cSettings for negative values


For our example, the values in the ‘Format Object’ dialog box are shown in fig
19.11.1c. The Target Cell will have the formula
= 100 - $F$27
Properties & Methods in Macros

In section 19.5.1 we have seen how we can link a control placed on a worksheet
with an object in our macro’s code. Here is the code for a ScrollBar
Dim scrlCashDiscount As ScrollBar

Set scrlCashDiscount = wks.ScrollBars(“scrlCashDiscount”)

Properties
1. LinkedCell: This property holds a string value containing the address of the
cell that holds the value of the ScrollBar. It is set via the ‘Cell link’ field in the
‘Fromat Control’ dialog box.

The following code snippets show the usage of this property.

a) Printing the current value

Debug.Print scrlCashDiscount.LinkedCell

b) Setting to a cell on the same or different worksheet

scrlCashDiscount.LinkedCell = “MasterData!$AK$1”

c) Setting to a named range(name can refer to a cell or a formula that returns a
single cell)
scrlCashDiscount.LinkedCell = “DummyLinkCell”

The Name ’DummyLinkCell’ is name of a range.

2. Value: This property returns an Integer datatype that holds the value of the
ScrollBar. Its value is between the values set for Min and Max properties. This
property corresponds to the ‘Current Value’ property in the ‘Format Control’
dialog box.

3. Min: This property returns an Integer datatype that holds the least possible
value of the ScrollBar. It corresponds to the ‘Minimum Value’ that we set in
the ‘Format Control’ dialog box.

4. Max: This property returns an Integer datatype that holds the largest possible
value of the ScrollBar. It corresponds to the ‘Maximum Value’ that we set in
the ‘Format Control’ dialog box.

5. SmallChange: This property returns an Integer datatype that holds the
increment that ScrollBar makes when one of the scroll arrows is clicked. It
corresponds to the ‘Incremental Change’ property that we set in the ‘Format
Control’ dialog box.

6. LargeChange: This property returns an Integer datatype that holds change in
value of the ScrollBar when user clicks between the scrollbox and one of the
scroll arrows. It corresponds to the ‘Page Change’ value that we set in the
‘Format Control’ dialog box.

Code below shows how to read and set the Value and Max properties. The
syntax will be similar for Min, SmallChange and LargeChange properties.
Dim val, maxval As Integer

Property Name Code
Value Read Val = scrlCashDiscount.Value
Set scrlCashDiscount.Value = 10
Max Read maxval = scrlCashDiscount.Max
Set scrlCashDiscount.Max = 850


Note for properties 1 to 6: Although we can set these properties in macros, it
is advisable to set these properties only in the ‘Format Control’ dialog box
when we are designing our form and then, only read their values when
working in a macro.
Methods
This control does not have any method of much practical use. Available methods
deal with positioning of the control on the screen. We will not explore them as it
is better not to move the controls from within a macro once a form is designed.


Events
· Change: This is the only event available for this control. This event gets
triggered whenever the user changes the value of the ScrollBar by
· Clicking on one of the Scroll Arrows
· Moving the ScrollBox
· Clicking between the ScrollBox and one of the arrows

The macro that will run in response to this event can be created as discussed
in section 19.5
19.12 Spin Button
Spinbutton is a control that provides a way to select one numeric value from a
range of values but, only in fixed steps. This is unlike the ScrollBar where the
user could select a proportionately from a range of values. Spinbutton control
can be placed on a worksheet by clicking on the icon marked in the following
figure.

Name of the control can be set using method shown in section 19.3. User can
change the value of the control by clicking on up or down arrows.
Next we will set the essential properties of a SpinButton. Refer figure 19.12a.

Fig 19.12a Properties for a SpinButton

Important Note:
Referring to Fig 19.12a
All these values are positive integers. We cannot have fractional values or
negative values. We will not set the ‘Current Value’ property. We can leave it at
default and let it vary as the control is used.

a) Maximum Value: This is the largest value the control is allowed to have.
This value must be greater than the Minimum value. Largest value allowed is
30000.

b) Minimum Value: This is the smallest value the control is allowed to have.
This value has to be less than the Maximum value. Least value allowed is 0.

c) Incremental change: The amount by which value of the control will change
when the user clicks once on one of the arrows.

d) Cell link: Address of the worksheet cell that will hold the control’s value.

Fig 19.12b Orientation and buttons of a SpinButton

Important Note:
On a worksheet a SpinButton can be placed only with a vertical orientation as
shown in the adjoining figure on the right.
Clicking the UpArrow moves the value towards ‘Maximum Value’.
Clicking the Down Arrow moves the control’s value towards ‘Minimum Value’

19.12.1 Fractional/Negative values & reversing the


direction
We can use the exact same techniques that we used in section 19.11.1 for the
ScrollBar control. No change is required what so ever.
Properties & Methods in Macros
In section 19.5 we have seen how we can link a control placed on a worksheet
with an object in our macro’s code. Here is the code for a SpinButton.
Note that in macros, the SpinButton is available in an object called Spinner. A
worksheet holds all the SpinButtons in a collection called ‘Spinners’
Dim spbtnSpecDiscount As Spinner

SetspbtnSpecDiscount= wks.Spinners(“spbtnSpecDiscount”)

Properties
1. LinkedCell: This property holds a string value containing the address of the
cell that holds the value of the SpinButton. It is set via the ‘Cell link’ field in
the ‘Fromat Control’ dialog box.

The following code snippets show the usage of this property.
a) Printing the current value

Debug.Print spbtnSpecDiscount.LinkedCell

b) Setting to a cell on the same or different worksheet

spbtnSpecDiscount.LinkedCell = “MasterData!$AK$1”

c) Setting to a named range(name can refer to a cell or a formula that
returns a single cell)

spbtnSpecDiscount.LinkedCell = “DummyLinkCell”

The Name ’DummyLinkCell’ is name of a range.

2. Value: This property returns an Integer datatype that holds the value of the
SpinButton. Its value is between the values set for Min and Max properties.
This property corresponds to the ‘Current Value’ property in the ‘Format
Control’ dialog box.

3. Min: This property returns an Integer datatype that holds the least possible
value of the SpinButton. It corresponds to the ‘Minimum Value’ that we set in
the ‘Format Control’ dialog box.

4. Max: This property returns an Integer datatype that holds the largest possible
value of the SpinButton. It corresponds to the ‘Maximum Value’ that we set in
the ‘Format Control’ dialog box.

5. SmallChange: This property returns an Integer datatype that holds the
increment that SpinButton makes when one of the arrows is clicked. It
corresponds to the ‘Incremental Change’ property that we set in the ‘Format
Control’ dialog box.

Code below shows how to read and set the Value and Max properties. The
syntax will be similar for Min and SmallChange properties.
Dim val, maxval As Integer

Property Name Code


Value Read Val = spbtnSpecDiscount.Value
Set spbtnSpecDiscount.Value = 10
Max Read maxval = spbtnSpecDiscount.Max
Set spbtnSpecDiscount.Max = 850

Note for properties 1 to 5: Although we can set these properties in macros, it
is advisable to set the property in the ‘Format Control’ dialog box when we are
designing our form and then, only read these properties when working in a
macro.
Methods
This control does not have any method of much practical use. Available methods
deal with positioning of the control on the screen. We will not explore them as it
is better not to move the controls from within a macro once a form is designed.
Events:
· Change: This is the only event available for this control. This event gets
triggered whenever the user changes the value of the SpinButton by. Clicking
on one of the arrows

The macro to run in response to this event can be created as discussed in
section 19.5.

Chapter 20
Creating Visual Basic UserForm
In this chapter we will discuss how we can create a form using the Visual Basic
UserForm.
We will create similar to the one we created in Chapter 19, using the exact same
controls.
20.1 Setting up the Userform & adding controls
First we need to add a UserForm in our VBA project. For doing this, right click
in the ‘Project Explorer’ window.
From the context menu that comes up, select ‘Insert’ and then select ‘UserForm’.
Excel provides us two things as shown in figure 20.1b:
1. A blank UserForm

2. A palette called ‘ToolBox’ which contains all the controls that we can add on
the form.

Fig. 20.1a Adding a UserForm

Whenever a UserForm is opened in the Visual Basic Editor such that we can add
controls and make changes to the form and its controls, the form is said to be
open in the Design Mode
If at any time we close the ToolBox palette, we can view it again by selecting the
‘Toolbox’ option in the ‘View’ menu.
Changing the size of a UserForm:
We can select the whole form by clicking on any area that does not contain a
control. Best way is to click on the ‘Title Bar’, the area having the name of the
form (“UserForm2” in figure 20.1b).
Once selected, eight small boxes (aka. ‘Handles’) appear. Four at the corners and
four on each side. Dragging these with our mouse, we can increase or decrease
the area of the form.

Fig. 20.1bToolbox palette & Adding controls

To add a control to a form:


1. Click on the control’s icon in the Toolbox and move the mouse pointer over to
the form.
The mouse cursor will change a cross hair.

2. Click and hold at the location where the control’s top left corner would
appear.

3. Draw a rectangle by dragging the mouse.
Selecting and Adjusting size/position of a control:
A control can be selected just by clicking on it.
Once selected, a thick border and eight small boxes appear around the control
(refer to figure 20.1b)
When we hover over the boxes or ‘Handles’, the mouse cursor changes to a
double headed arrow. We can click on the boxes and drag to change the size of
the control.
When we hover our mouse on the border, but between the handles, the mouse
cursor changes to a Cross with arrows. We can click and while holding the left
mouse button drag the mouse to change the position of the control on the form.
Making changes to existing UserForm:

To make changes to a UserForm that’s already been created, we have to open the
form in the Design Mode. To do this, go to the VBE and in the Project Explorer
window, locate the UserForm and double click on it.
The form opens in Design Mode and we can make the required modifications.
20.2 Setting names and properties
In order to refer to the UserForm, and controls that we will place on it, from
within our macros, we need to set unique names for our UserForm and the
controls.
First, we need to bring up the ‘Properties Window’ which is visible by default
when the Visual Basic Editor opens up.
If the Properties window is not visible, click on the ‘View’ menu and then select
‘Properties Window’. Or we can press ‘F4’. A typical view of the Properties
window is shown in figure 20.2a. In this case it is for an OptionButton control
which we will see in detail later.
The procedure for doing this is general, so we will discuss it in this section. It
will not be repeated when we see individual controls.
A long list of properties is available which we can set. But we are mainly
concerned only with a select few related to Name and appearance of the controls.

Fig. 20.2a Properties Window

Most important properties are as follows:


1. Name: This is the name of the control that will be used when we are referring
to the control in our macros. So, it is advisable to give it some meaningful
name that denotes the type of control and its purpose.

2. Font: It is the font of the text that will appear in the control.

3. Caption: It is the text that will appear as a label in the control. This property
is available only for the OptionButton and the CheckBox controls.
In case of a UserForm, the Caption is the title that appears at the top of the form.
For example, in figure 20.1b, the caption is ‘UserForm2’
The properties related to data coming to and from the control will be set in our
macros. In that case the properties of interest will be the ‘RowSource’ and the
‘Value’ properties. We will not set them from the ‘Properties Window’.
Ensure that the ‘Locked’ property is always ‘False’. If for a control this property
is set to true, the user will not be able to edit data in that control even though
user can position the mouse cursor inside the control.
20.3 Focus, Tab Index & Tab Stop
We will discuss an important concept and one related property that we didn’t
cover in the previous section.
Focus:
Before our users can enter data into a control, they have to navigate to the
control. This can be done either by clicking on the control with the mouse, or
navigating to the control by pressing the ‘Tab’ key for the required number of
times.
Once a user has navigated to the control, the control is said to have ‘received
Focus’. The operating system directs all inputs from the keyboard and mouse to
that control.
We can compare this situation to moving to a cell on a worksheet either by
clicking or using arrow keys. The current active cell is said to have the focus.
When the user moves to another control, the current control ‘loses Focus’ and
the other control ‘receives Focus’
The interesting fact for us is that the operating system notifies the user as well as
our macros that a control has received or lost focus. This allows us to build
features to help our users with visual cues for entering data (remember our goal
of User-Friendliness). We will see examples when we discuss Events.
Tab Index & Tab Stop:
Problem:
Our users have to enter a large amount of data one record at a time. We would
like the data to be entered in a set order. For example, values for discount should
not be set until an item has been selected. Selecting an item should be done only
after selecting a customer.
That means users have to repeatedly move their hand from keyboard to mouse in
order to move between different controls. This increases the time and effort
require for data entry.
Solution:
Recall that Windows allows us to move between controls using the ‘Tab’ key.
Along with this, each control has a property called ‘Tab Index’ that allows us to
set the order in which focus will move from one control to another on a form.
This is marked in figure 20.2a along with the ‘Tab Stop’ property.
Tab Index is an integer value given to each control placed on a user form. The
lowest allowable index is zero. The highest value is one less than the number of
controls on the form.
When the form is first displayed, the control with the lowest tab index receives
the focus. When the ‘Tab’ key is pressed, focus moves to the control with the
next higher Tab Index.
We can set the Tab Index in the order in which user will logically enter data.
This becomes important when user is entering data by reading from a sheet of
records.
The ‘Tab Stop’ property of a control dictates if focus will arrive (‘Stop’) at that
control when user is navigating by pressing the ‘Tab’ key repeatedly.
If we want to prevent input to a particular control, like a TextBox (may be
because its value will be set by our macro), we should set its ‘Tab Stop’ property
to ‘False’. The control will retain its ‘Tab Index’ property, but it will never
receive Focus through the Tab key.
20.4 Link to macros
When writing macros that work with a UserForm and its controls, the following
points should be remembered:
1. Each UserForm becomes a named object available to our macros.
The name is same as the name we set in the ‘Properties’ window of the
UserForm in section 20.2.

We can treat this object as any other object in VBA. We can read and set its
properties and we can call its methods.

For example, if our UserForm is named “ufOrderForm” then the following
code will print its caption in the immediate window.

Debug.Print ufOrderForm.Caption

2. Each control on the UserForm becomes a named property of the UserForm
object.
Name of the control is the name we set in the ‘Properties’ window of the
control in section 20.2
So if in our UserForm we had an option button named ’rbtnNorth’ then we
can access that option button using the following syntax.
ufOrderForm.rbtnNorth
Once the object is obtained, we can access its properties and methods using
another dot (.) operator.
The ‘With’ block comes in handy in such situations to avoid typing the same
form and control names repeatedly. The following code prints out the caption
and value of our optionbutton.
With ufOrderForm.rbtnNorth
Debug.Print .Caption
Debug.Print .Value
End With



20.5 UserForm
The UserForm that we created in an earlier section becomes a named object for
our macros. Let us look at some important properties and methods of this object.
Properties
1. Caption: This property holds a string representing the title that appears at the
top of the form’s window. For example, in figure 20.1b, the caption is
“UserForm2”.

2. Visible: This property holds a Boolean value that specifies whether or not the
UserForm is visible on the screen. The following code first prints the
UserForm’s caption and then hides the UserForm from the screen.

With ufOrderForm
Debug.Print .Caption
.Visible = False
End With

Important Note:
When the visible property is set to False, a user cannot interact with the
UserForm but a macro can. All the controls will hold the most recent values they
had just before the form was hidden. We can read and manipulate the values and
properties of controls from our macro.

When the UserForm is again shown (by setting its Visible property to True), the
controls will have the most recent value (either the ones set by our macro after
hiding the form, or ones they had when the form was hidden)

The Visible property can be set only when the UserForm has been displayed on
the screen by using the Show method (described next).
Methods
1. Show: This is the only useful method for a UserForm. It displays the
UserForm on screen so that the user can interact with it. Here is the syntax for
the method:

<UserForm object>.Show([modal])


Arguments:
The modal argument controls what happens after the form is displayed on
screen. Following table describes the effect of the two values that the
argument can take

Value Display Mode Effect
0 Modeless · User cannot interact with any other part of
Excel
· Macro executes to the next line of code
coming after ‘Show’ method
1 Modal · This is the default value.
· User cannot interact with any other part of the
application (Excel) until the form is closed.
· Macro execution stops, the line of code after
‘Show’ method is not executed until user
closes the form.

Before we use this method, we should
a) Hide any controls that we want to be initially hidden
b) Set any default values for controls

ufOrderForm.Show

It is always better to show the form as modal so that other parts of the
spreadsheet are not accidently changed. This way we can ensure better
control of the code execution and user interaction.
20.6 Common Properties of Controls
In this section we will see some properties that are available for all the controls.
Any property that is specific to a particular control will be taken up when we
discuss individual controls.
Here are those properties:
1. BackColor: This property has a Long Integer value denoting the background
color of the control. A related property is the ‘ForeColor’ property that denotes
the color of the text displayed on a control.

As far as possible we should not change these in a macro. But, in certain
cases, these properties can be used to provide visual cues to the users. Like
when we want to disable a control.

We will see examples and syntax for the BackColor property in an upcoming
section. The code for ForeColor will be the same.

2. Visible: This property has a Boolean value and allows us to show or hide the
control on the screen from within our macro. If the value of this property is set
to TRUE, the related control is visible. A value of FALSE hides the control
from view and the user cannot interact with it.

For a ScrollBar (named ’scrlCashDiscount’) control, the syntax to hide that
control would be:

ufOrderForm.scrlCashDiscount.Visible = False

The syntax to show it again would be:

ufOrderForm.scrlCashDiscount.Visible = True

By default, a control is always visible. So in case we want to hide the control
when the form is first displayed, we should set the control’s ‘Visible’ property
to ‘False’ before we display the UserForm using the ‘Show’ method. Mostly
this is done in the ‘Initialize’ event of the UserForm and is described in an
upcoming section.

3. Enabled: This property has a Boolean value and allows us to enable or
disable a control from inside a macro.
When a control is disabled, it is visible on the screen, but it cannot respond to
user actions. User cannot change the value of the control.
For our ScrollBar control, the syntax to disable that control would be:

ufOrderForm.scrlCashDiscount.Enabled = False

The syntax to enable it again would be:

ufOrderForm.scrlCashDiscount.Enabled = True

Important note:
A disabled control is a bit confusing because it appears on a form as a normal
control. But when the user clicks on it nothing happens. This might cause a
confusion for the user.
We have two options if we want to prevent input to a control,
1. Set the visible property to ‘False’ or
2. Disable the control and also sent its background color to a different one.
The code below shows how we can do this.
Here we have set the background color to a predefined VB constant. This is
the only time we will be using the ‘BackColor’ property from within a macro.

With ufOrderForm.lbProdList
.Enabled = False
.BackColor = vbActiveBorder
End With

Now if we want to enable the control again, we can use the following code
Fig 20.6 shows the effect of this code on two list boxes.

With ufOrderForm.lbProdList
.Enabled = True
.BackColor = vbWindowBackground
End With

In the code above we have used two System color constants
1. vbActiveBorder: Color of border of an active form.
2. vbWindowBackground: Default window background color
These two constants can be used anywhere in our code where we have to
specify colors. However, their effect is system dependent. So it is better to test
the effect by executing the code once.



Fig 20.6 Disabled Listboxes, with changed background colors


Some other Visual Basic color constants that we can use are:
· vbRed
· vbBlack
· vbGreen
· vbYellow
· vbBlue
· vbCyan
· vbWhite
But none of these provide the dimmed visual effect required by a disabled
control. Hence we had to use the System Color constants.

4. Caption: This property has a string value and allows us to read and set the
caption of the control. (Note: This property is available only for Button,
OptionButton and CheckBox)
For our OptionButton control, the syntax to print the caption is:
Debug.Print ufOrderForm.rBtnNorth.Caption

The syntax to set a new value for the caption is:


ufOrderForm.rBtnNorth.Caption = “Please select a value”

Here ’rBtnNorth’ is an OptionButton object that refers to an OptionButton


named ‘rBtnNortn’ placed on a worksheet.

5. Value: This is the most important property for a control. It holds the data that
the user has selected or entered into the control. Following table shows what
the ‘Value’ property will hold for various controls
Control DataType Represents
TextBox String Text entered in the textbox.
ListBox String For a Single selection ListBox displaying a
single column, it is the text currently selected in
the ListBox.

This property has no significance for a ListBox
that allows multiple selection
SpinButton Long A value between the values set for Minimum
and Maximum properties.
ScrollBar Long A value between the values set for Minimum
and Maximum properties.
CheckBox Boolean / True if the box is checked, False otherwise. If
NULL TripleState property is set, a third possible
value is NULL
OptionButton Boolean True if the button is selected, False otherwise
ComboBox String For a ComboBox showing a single column of
data, it is the text that is currently selected in
the ComboBox.
20.7 Events for Controls
In this section we will see the general method for creating ‘Events’ for Controls.
We will also discuss some important Events that are common across various
controls so that we don’t have to repeat them again when we discuss individual
controls.
An ‘Event’, when talking about controls, can be thought of as a specific macro
that runs when the user does something to the control.
Events are named after the action that the user takes on the control. For example,
when the user ‘Clicks’ on a control, the ‘Click’ event is triggered and the
associated macro runs.
A UserForm and, each of the controls placed on it, allow us to create several
event macros. We will see some of the common and useful events in this section.

20.7.1 Setting an Event


Let us see how we can set an event for a control or UserForm.
Figure 20.7.1a shows the process for creating an event for a ComboBox control
placed on a UserForm.
1. Select the UserForm or control (as described in section 20.1 “Setting up the
UserForm and adding controls”)

2. Right click and select ‘View Code’
A new window opens up which is a code module for the entire UserForm that
contains all the code that is specifically related to the UserForm and all the
controls contained therein.

Fig 20.7.1a Creating an event for a control

Figure 20.7.1b shows a code module for a typical user form.


Fig 20.7.1b Code module for a UserForm

The two important parts of this module are:


a) The ‘Object’ selection dropdown. Marked as number 1

This dropdown has a list containing all the controls placed on the UserForm
and the UserForm itself.

b) The ‘Procedure’ selection dropdown. Marked as number 2
This dropdown has a list containing all the events that can be created for the
control selected in the ‘Object’ selection dropdown.
Creating an Event macro for a control happens in two steps:
1. We select the control for which we want to write an event macro.
2. We select the Event that we want to create for the selected control.
When we select the Event, Excel automatically creates an empty event sub
procedure.
The name of the subprocedure has the format
<Control Name>_<Event Name>.
Figure 20.7.1b shows one such sub procedure created for the ComboBox names
‘cbProdList’ and its ‘Change’ event.
The word ‘Private’ is important and prevents any other code from calling the sub
procedure from some module outside of the UserForm. This ensures that the
event macro is run only when the user takes the related action on the control.
If we try to run that sub procedure from an outside module like so,
Call ufOrderForm.cbProdList_Change()
our macro will report an error.
All event procedures are created inside the code module of the UserForm that
holds them so we don’t have to use the form object to reference them in each
line of code.
Meaning we can directly write :
cbProdList.Visible = False
Instead of writing :
ufOrderForm.cbProdList.Visible = False

The event procedures inserted by Excel are preceeded by a work ‘Private’.


This signifies that the procedure can be called only by other procedures inside
the same form module. Using this keyword requires slightly advanced
knowledge of Object Oriented Programming and we will not delve into that.
The only thing we need to be cautious about is not to change anything in the
lines automatically inserted by Excel.

20.7.2 Common Events


In the following discussion when we say an Event is triggered, we mean that the
associated macro is executed.
Each control provides a long list of Events for which we can write macros.
However, in this book we will focus on only the few most useful and commonly
used Events. The reason being:
1. Many of the events like MouseUp/MouseDown, MouseMove etc are useful if
we want to provide advanced graphics functionality like drawing on screen or
‘drag & drop’.
These are not usually required in an application related to capturing data.

2. Most of Events we will discuss are preceded & followed by other events.
For example, when a user clicks on a control, events are triggered in the
sequence: a) MouseDown b) Change c) Click d) MouseUp


Hence for most purposes we can smartly combine our code in the ‘Change’ or
‘Click’ events instead of writing 4 different pieces of code.

3. It would be annoying for the user if we keep executing macros and
notifications for each and every micro action taken by the user.
The following is a list of Events that are common to all the controls. If there is
any event that is specifically useful just for a particular control, we will cover it
when we discuss individual controls.
The events are listed in the order in which they happen during a usual user
interaction.
1. Enter: This event is triggered when a control receives focus. The best use of
this event is to:
a) Display a message to the users to help them with what data to enter.

b) Change the display of a control slightly so that if the user is interrupted
while entering the data, on coming back they will know exactly where they
left off.
Fig 20.7.2a TextBox with focus

In the UserForm that we are developing, there is a TextBox for entering the
quantity of items ordered. In the ‘Enter’ event for this control, we will set its
‘BackColor’ property to Yellow.
Figure 20.7.2a shows the TextBox when it has received focus.
Let’s see the code for this event. The name of the textbox is set to ‘txtQuantity’.
Before we change the background color, we will store the current value of the
color in a variable (module level variable declared outside the sub). This will
help when we have to reset the color back to the original value.
Dim prevColor as Long
Private Sub txtQuantity_Enter()
prevColor = txtQuantity.BackColor
txtQuantity.BackColor = vbYellow
End Sub

2. Change: Triggered when the value of a control changes. Like when the user
selects a new item in a ComboBox or ListBox. Or when user checks or
unchecks a check box or selects another OptionButton in an option group.
Examples and sample code for this event are given a bit later in the portion
titled “What we can achieve with Events”



Important Notes:
It should be noted that the new value is available within the event macro.
Hence we can first check the new value and then take appropriate action.

For a multi-selection ListBox, this event gets triggered whenever an
additional item is selected.

For a TextBox control, the ‘Change’ event is triggered every time the user
types in a new character, so it is not advisable to write a code for this event
for a TextBox.

3. Exit: triggered when a control loses focus. Best way to use this event is as
follows.
a) Check the value entered by the user. If the value is not acceptable, display
a message and bring the focus back to the control so that user can enter the
correct value.

b) Reverse the change made in the Enter event if any.

Recall the TextBox control we saw when discussing the ‘Enter’ event (Fig.
20.7.2a). Suppose the user has entered a value and is now moving on to
another control. The TextBox will lose focus.

We want to ensure that if the user has entered a non-numeric value, we should
display a message and the focus should remain in the TextBox. The code
below shows how we can handle this in the TextBox’s Exit event.

The Exit event has the following signature

Private Sub <Control Name>_Exit(
ByVal Cancel As MSForms.ReturnBoolean)

The argument ’Cancel’ takes a Boolean value and dictates what happens after
the Event macro is executed.
If we set ’Cancel’ to True, focus stays within the current control.
If we set ’Cancel’ to False, focus control loses focus.
If user has entered a numeric value, we will make the control lose focus and
reset the background color to the value we saved earlier in the ‘Enter’ event.
Private Sub txtQuantity_Exit( _
ByVal Cancel As MSForms.ReturnBoolean)
If Not (IsNumeric(txtQuantity.Value)) Then
MsgBox "Please enter a numeric value for Quantity"
Cancel = True
Else
‘//** User has entered a valid value, **//
‘//** reset the background color to the saved value. **//
‘//* No need to retain focus. **//
Cancel = False
txtQuantity.BackColor = prevColor
End If
End Sub

4. Initialize: This event is available only for a UserForm. It is triggered just
before the form is shown on the screen. At this stage all the controls are
available as properties and we can operate upon them. The best use of this
form is to
a. Hide the controls that will be displayed later based on some action of the
user
b. Set default values for controls.
c. Set the source for listboxes and comboboxes
The code below shows how the Initialize event can be used. The various
controls and their properties will be described in upcoming sections.
Private Sub UserForm_Initialize()
‘//** Set the source for List and Combobox **//
lbProdList.RowSource = _
"[Book1Notes.xls]Sheet2!$B$1:$B$9"

cbProdList.RowSource = "Sheet4!AG2:AG5"

‘//** Set one of the checkboxes to NULL value **//
chkSpecDiscount.Value = Null

‘//** Set the value for one of the OptionButtons. **//
opbtnNorth.Value = True
End Sub

What can we achieve with Events:
1 Set the state (Visible/Enabled) of one control based on value of another
control.

2 Display a message to the user about the consequences of entering certain type
of data and also about what data will be captured.
Fig 20.7.2 Disabled Listboxes & changed background colors
The code that follows is for the ‘Change’ event of a CheckBox we placed on our
order form.
That checkbox is pointed out in figure 20.7.2. Name of the CheckBox is
‘chkAddMultipleItems’.
When the form is displayed initially:
1. The two ListBoxes on our UserForm are disabled.
2. The CheckBox does not have a checkmark.

If the user puts a checkmark in the CheckBox, a message is displayed to the user
in a message box.

When the user clicks on the ‘OK’ button of the message box, the two ListBoxes
are enabled. When the user clears the checkmark, the ListBoxes are again
disabled.

The following code illustrates points 1 and 2 discussed so far.
Private Sub chkAddMultipleItems_Change()
If chkAddMultipleItems Then
MsgBox "You are about to enter multiple items" & _
vbCrLf & _
"Items will be added with standard quantities" & _
vbCrLf & _
"Special Discount will be applied to all items"

lbProdList.Enabled = True
lbProdList.BackColor = vbWindowBackground
lbSelItemList.BackColor = vbWindowBackground
Else
lbProdList.Enabled = False
lbProdList.BackColor = vbActiveBorder
lbSelItemList.BackColor = vbActiveBorder
End If
End Sub

3 Set the value of cells on a worksheet using value of a control (say a ScrollBar
named scrlCashDiscount). Here is the code.

Wks.Cells(3,25).Value = scrlCashDiscount.Value

4 Set the value of one control based on value in another.

5 Check the value of the current control (one for which the event is triggered)
and limit the value if required

Suppose on our form we have
Control Name Features
SpinButton spnValue Maximum value = 100, Minimum value= 10
TextBox txtValue Its value is set to the value of the SpinButton
CheckBox chkCeiling If the box is checked, value of SpinButton is not
allowed to exceed 75.

Following code illustrates points 4 and 5 discussed above.

Private Sub spnValue_Change()

If chkCeiling.Value And (spnValue.Value > 75) Then
MsgBox ("Value can’t exceed75")
spnValue.Value = 75
End If
txtValue.Value = spnValue.Value
End Sub

20.8 ListBox
Listbox is a control that presents a list of items to the user. User can scroll
through the list and can select one or more items (depending on the settings of
the control). Listbox control can be placed on a UserForm by clicking on the
icon shown in the following figure.

When placing the control, it’s better to draw a rectangle high enough so that at
least 8-10 lines of items are visible.
We have already seen how to set the name for a control in section 20.2
Features related to different values of ‘MultiSelect’ Property
MultiSelect Features
fmMultiSelectSingle User can select only one value from the list.
fmMultiSelectExtend User can select the first item by clicking on it.
After that, if user clicks on any other item, that item
becomes the selected item, the first item is discarded.
To select additional items, the user has to hold down
the ‘Ctrl’ key and make subsequent selections.
To deselect an item, the user has to hold down the
‘Ctrl’ key click on that item again.
fmMultiSelectMulti User can the first item by clicking on it.
After that, if user clicks on any other item, that item
also gets selected
To deselect an item, the user has to click on that item
again.

Properties & Methods in Macros
Properties
Now we will see some other important properties specific to a ListBox.
1. RowSource: This property holds a string value containing the address or
Name of the cell range that has the data which will be listed in our ListBox.
The following code snippets show the usage of this property.

a) Printing the current value

Debug.Print lbProdList.RowSource

b) Setting to a range on the same or different worksheet

lbSelList.RowSource = "MasterData!$B$2:$B$9"

c) Setting to a named range(name can refer to a cell or a formula that returns a
single column)

lbProdList.RowSource = “ProdList”

The Name ’ProdList’ refers to a formula “=INDEX(ProdDB,,2)”

The Name ’ProdDB’ and ProdList’ are named ranges we created in section
18.3.1.

2. List: This property holds an array of all the items in the ListBox. The returned
array is of Variant datatype. Each item in the list has an index position. The
first item has the index position of ‘0’. The item at the position ‘i’ is given
by:

lbProdList.List(i)

If a multicolumn range was specified in the ‘Input range’ property, then only
the first column is returned as an array in the ‘List’ property.

The code below loops through and prints all the items in a ListBox. Notice
how we have avoided the problem of not knowing lower bound of the returned
array.



Dim startPos, endPos As Long
startPos = LBound(lbProdList.List)
endPos = LBound(lbProdList.List)

For i = startPos To endPos Step 1

Debug.Print lbProdList.List(i)
Next i


The values held in the array are actually Strings (if the entries are
alphanumeric) or Double or Integers based on the values held in the range that
we provide in the ‘RowSource property.

Fig 20.8b

For example, if we specify the range shown in figure 20.8b, we will be able to
perform mathematical oparations on all the values except for the 3rd and 7th
values.

Hence we need to be careful that the range we specify has only values of the
same datatype. These should match the operations we want to perform on the
values in our macro.


3. ListCount: This property returns a Long integer value showing the total
number items available in the ListBox. We can only read this property. Syntax
is as follows:

lbProdList.ListCount


4. Selected: This property holds an array of Boolean values. The length of the
array is the same as the array returned by the ‘List’ property.

The item at row ‘i’ in the array is given by lbProdList.Selected(i). The value of
the item is TRUE if the item has been selected by the user, else it is false.
In a single selection ListBox only one item will be TRUE, while in a multi
selection ListBox, multiple items will have a TRUE value.

The following code loops through all the values in the ListBox and prints only
the selected values.
Dim startPos, endPos as Long
startPos = LBound(lbProdList.List)
endPos = LBound(lbProdList.List)
For i = startPos to endPos step 1
If (lbProdList.Selected(i)) Then
Debug.Print lbProdList.List(i)
End If
Next i


5. Value: This property returns a Variant datatype that holds the value of a single
selection ListBox. This property cannot be used when the selection type of the
ListBox is set to multi/extended types.

‘Value’ in this case refers to the text is displayed in the ListBox. The code
below prints out the current value of the ListBox
Debug.Print lbProdList.Value

Methods
1. AddItem: This method is used to add items in a ListBox whose Input range
property has not been set. The syntax is as follows:

lbProdList.AddItem(item, position)

Arguments
a) Item: this should be a string or number. It specifies the value to be added in
the ListBox.
b) Position: this is an integer specifying the position where the new item has
to be added. This is an optional argument, omitting it would add the item in
the bottommost position in the list.
We should ensure that the ‘RowSrouce’ property is not set for the ListBox
for which this method is being called. Otherwise, the macro reports an error
and execution stops.
2. Clear: This method deletes all the items from a List box.
If the ‘RowSource’ has been specified, our macro stops after reporting an
error. So we should make sure we don’t invoke this method on a ListBox for
which the ‘RowSource’ has been specified.

lbProdList.Clear

3. RemoveItem: This method allows us to remove an item from a particular
location in a list box for which the ‘RowSource’ property is not specified.
The syntax is:

lbProdList.RemoveItem(index)

The argument index specifies the row that we want to delete from the ListBox.
We should ensure that the ‘RowSource’ is not specified for the ListBox for
which this method is being called or else the macro displays an error and
hangs.
Events
· Change: This event gets triggered as follows:

a) Selection Type is set to ‘Single’: In this case the event occurs whenever the
user selects an item hence causing the ‘Value’ property to change.

b) Selection type is set to ‘Multi’ or ‘Extend’: Event occurs whenever the user
clicks in the ListBox to either select or deselect an item.

For this control, the code for ‘Change’ event along with ‘Enter’ and ‘Exit’ events
can be created as detailed in section 20.7.
20.9 ComboBox
ComboBox is a control that presents a list of items to the user in a dropdown list.
User can scroll through the list and can select only one item. A Combobox
control can be placed on a UserForm by clicking on the icon marked in the
following figure.

We have already seen how to set the name and other essential properties for a
control in section 20.2.
Now we will see some other important properties specific to a ComboBox. Note
that we will be accessing these properties through macros and not in the
Properties window.
1. RowSource: This property holds a string value containing the address or
Name of the cell range from which data is displayed in the ComboBox. The
following code snippets show the usage of this property.

a) Printing the current value

Debug.Print cbCustList.RowSource

b) Setting to a range on the same or different worksheet

cbCustList.RowSource = "MasterData!$N$2:$N$9"

c) Setting to a named range (name can refer to a cell or a formula that returns
a single column)

cbCustList.RowSource = “CustList”

The Name ’CustList’ refers to a formula “=INDEX(CustomerDB,,2)”
The Name ’CustomerDB’ and ’CustList’ are named ranges we created in
section 18.3.1.

2. List: This property holds an array of all the items in the ComboBox. The
datatype of the returned array is Variant.

Each item in the list has an index position. The first item has the index
position of ‘1’. The item at the position ‘i’ is given by:

cbCustList.List(i)

The code below loops through and prints all the items in a ComboBox. Notice
how we have avoided the problem of not knowing lower bound of the returned
array.
Dim startPos, endPos as Long
startPos = LBound(cbCustList.List)
endPos = UBound(cbCustList.List)
For i = startPos To endPos Step 1
Debug.Print cbCustList.List(i)
Next i

The values held in the array are actually Strings (if the entries are
alphanumeric) or Double or Integers based on the values present in the range
that we provide in the ‘RowSource’ property.

Fig 20.9b
For example, if we specify the range shown in figure 20.9b, then we will be able
to perform mathematical oparations on all the values except for the 3rd and 7th
values.

So we need to be careful that the range we specify has only values of the same
datatype. These should match the operations we want to perform on the values in
our macro.


3. ListCount: This property returns a Long integer value showing the total
number items available in the ComboBox. We can only read this property.
Syntax is as follows:

cbCustList.ListCount

4. ListIndex: This property returns a Variant datatype that holds the position of
the item currently selected in the ComboBox.
The first item in the list has an index of ‘0’. The largest value that this
property can take is ‘(ListCount - 1)’. The code below prints out the current
value of the ComboBox.

Debug.Print cbCustList.ListIndex

5. Value: This property returns a Variant datatype that holds the value of the
ComboBox.
‘Value’ in this case refers to what is displayed in the ComboBox. The code
below prints out the current value of the ComboBox
Debug.Print cbCustList.Value
Methods
1. AddItem: This method is used to add items in a ComboBox whose Input
range property has not been set. The syntax is as follows:

cbCustList.AddItem(item, position)
Arguments
a) Item: this should be a string or number. It specifies the value to be added in
the ComboBox.
b) Position: this is an integer specifying the position where the new item has
to be added. This is an optional argument, omitting it would add the item in
the bottommost position in the list.
We should ensure that the ‘RowSource’ property is not specified for the
ComboBox for which this method is being called. Otherwise, the macro stops
executing and reports an error.

2. Clear: This method deletes all the items from a ComboBox.
If the ‘RowSource’ property has been set, the macro stops executing and
reports an error. The syntax is
cbCustList.Clear

3. RemoveItem: This method allows us to remove an item from a particular
location in a ComboBox. The syntax is

cbCustList.RemoveItem(index)

We should ensure that the RowSource property is not specified for the
ComboBox for which this method is being called or else the macro displays
an error and hangs.

Events
All the Events, described in section 20.7 (except for Initialize), can be created
for a ComboBox.
20.10 Caveat about setting RowSource
In the previous sections we saw how we can set the RowSource property for a
ListBox and ComboBox. In those sections we had assumed that the workbook
that contains the form is the one that is currently displayed on the screen (the
ActiveWorkbook). However, if our macro works on multiple workbooks, it is
possible that a different workbook may be active on the screen and the one
containing the form or range for RowSource may be minimized.
In such cases the string specifying the RowSource should contain the name of
the workbook containing the range that we are using for the RowSource. The
following lines of code show how we can do this
UserForm1.lbProdList.RowSource = _
"'[" & ThisWorkbook.Name & "]MasterData'!B2:B9"


UserForm1.cbCustList.RowSource = _
"'[" & ThisWorkbook.Name & "]MasterData'!$N$2:$N$9"

Here the code that sets the range for RowSource is in the same workbook the
range itself. A detailed explanation on the format in which the string on the right
has to be formed is given in section 3.6.1 “Formulas referring to another
workbook or worksheet”
20.11 CheckBox
Checkbox is a control that provides a choice for one specific item. The user can
put a check mark in a checkbox if the item is desired. Checkbox control can be
placed on a worksheet by clicking on the icon marked in the following figure.

If the user puts a check mark in the box by clicking on it, the checkbox control’s
value is ‘True’. If the box is unchecked, the control’s value is ‘False’.
Common properties discussed in section 20.6 are all applicable to this control.
We will discuss here one property that is specific to a CheckBox.
TripleState:
This property holds a Boolean value and if set to ‘TRUE’ allows the use of
‘NULL’ as a value for the CheckBox. It is set in the Properties window and is
marked in figure 20.11a
If set to ‘FALSE’ a CheckBox can have only two values ‘TRUE’ and ‘FALSE’.
Most common applications have TripleState set to False.
Fig 20.11a Properties for a ListBox

The table below shows a checkbox with its TripleState property set to TRUE and
the different appearances of a check box and the value it can hold depending on
how the user interacts with the checkbox.
Appearance/State Value

Unchecked False
Null
(Checked & NULL
Dimmed)
Checked True

Note that we are setting this property in the Properties window, not from a
macro.

Properties & Methods in Macros
The TripleState property can be checked and changed from inside a macro.
If ufOrderForm.chkCashDiscount.TripleState Then

‘//** Triple state is true, take required action **//
End If

The TripleState property can be checked and changed from inside a macro.
Methods:
This control does not have any method of much practical use.
Events:
All the Events described in section 20.7 can be created for a CheckBox.
The code that follows is for the ‘Change’ event of a checkbox named
chkCashDiscount. It uses a ‘Select Case’ block to respond to different states of
the checkbox
Private Sub chkCashDiscount_Change()
Select CasechkCashDiscount Value
Case TRUE:
MsgBox "Box is checked”
Case FALSE:
MsgBox "Box is unchecked”
End Select
End Sub

Important notes when TripleState is set to ‘True’:
· If the CheckBox is currently in NULL state, then it cannot trigger events.
User has to click at least once to bring the value to either TRUE or FALSE.

· When the UserForm is first displayed, the CheckBox is unchecked. Clicking
on it sends it to NULL state. Clicking again takes the CheckBox to checked
state. Clicking again takes it to Unchecked state. Then the cycle repeats.

· If we want to set the CheckBox to NULL state when the form is first
displayed, we should set the value to NULL before we use the ‘Show’ method
on the UserForm. Here is the code for doing that:
If chkCashDiscount.TripleState Then

chkCashDiscount.Value = NULL
End If
20.12 OptionButton
OptionButton is a control that allows the user to select one item out of a fixed
list of items. If user selects any one item, the others are automatically deselected.
OptionButton control can be placed on a UserForm by clicking on the icon
marked in the following figure.

If the user selects an option by clicking on an OptionButton its value changes to


‘True’. If the option is not selected, the control’s value is ‘False’.
In section 20.2 we saw how we can set the name and some common properties
for a control. Same applies to an OptionButton.
We will now see a property that is specific for the OptionButton control. But,
before we proceed we need to get familiar with one term that we will need going
forward.
Option Group:
An option group is a list of items, presented to the user as a group of
OptionButtons, from which the user can select one item. When one item is
selected, other items in the group get automatically deselected.

Proper way of placing option buttons
When we put multiple OptionButtons on a UserForm they all become part of the
same option group.
But what if we want two or more different option groups on the same form? This
is where the ‘GroupName’ property of the OptionButton control comes in.
The GroupName property separates different option groups. All OptionButtons
having the same GroupName become part of the same option group. Figure
20.12a shows how to set GroupName in the Properties window.

Fig. 20.12a GroupName property

Using OptionButtons in macros


Inside our macros, the property that is most useful is the Value property. The
most useful event is the Click event.
The Value property of the optionbutton is a Boolean. It takes the value TRUE if
that particular optionbutton is selected. Any option button in the same group that
is not select will have the value FALSE.

Fig. 20.12b Sample option group

Figure 20.12b shows a group of OptionButtons on our Visual Basic UserForm.


The GroupName property has been set to grpRegion.
The following code is for the click event of one of the buttons. A procedure
declared in another module is called and the Value property is passed to that Sub.
Private Sub opbtnNorth_Click()

Call Module2.PostOptionSelect(opbtnNorth.Value)
End Sub

The following code checks the value of each optionbutton in a group and then
displays the choice. The code is in the ‘Click’ event of a CommandButton
(described next) placed on the same form as the OptionButtons.
Private Sub CommandButton1_Click()
Select Case True
Case opbtnNorth.Value
MsgBox "You selected North"
Case opbtnEast.Value
MsgBox "You selected East"
Case opbtnWest.Value
MsgBox "You selected West"
Case opbtnSouth.Value
MsgBox "You selected South"
Case Else
MsgBox "Please select a region first"
End Select
End Sub

20.13 CommandButton
CommandButton control can be placed on a worksheet by clicking on the icon
on the ToolBox marked in the following figure.

Name and Common properties of the control can be set using methods described
in section 20.2.
The most important properties of this control that we need to set in the properties
window are Name, the text appearing on the button- called the ‘Caption’ and the
‘Font’ of the caption.
The basic function of a CommandButton is to allow the user to activate or run a
piece of code when required. This is done by using the ‘Click’ event of this
control.
To use a CommandButton’s Click event, we open our form in the design view
and double click on the button. This opens up the code mudule of the button.
Since Click event is the default event for a button, Excel will automatically add a
blank sub for this event. If this is not visible select ‘CommandButton’ from the
left (object) drop down and select ‘Click’ from the right (procedure) drop down.

Now we can write the required code in this procedure. It is recommended that
we should call some other sub or function procedure from within this sub, this
sub should contain only the minimum required code.
Usage in Macros
Apart from the Enabled and Visible properties described in section 20.6, we
might want to control the Caption of a CommandButton from our macros. The
code below is just for illustration. It will toggle the caption between 2 values
when the button is clicked.
Private Sub CommandButton1_Click()
If (InStr(1, CommandButton1.Caption, "unclick", _
vbTextCompare) > 0) Then

CommandButton1.Caption = "Click to Click"
Else
CommandButton1.Caption = "Click to UnClick"
End If
End Sub

20.14 SpinButton
Spinbutton is a control that provides a way to select one numeric value from a
range of values but, only in fixed steps. This is unlike the ScrollBar where the
user could select a proportionately from a range of values. SpinButton control
can be placed on a worksheet by clicking on the icon marked in the following
figure.

Name and common properties of the control can be set using methods described
in section 20.2. User can change the value of the control by clicking on the
arrows of the SpinButton.
We will see how we can set some other important properties specific to a
SpinButton control. Before that, let’s clear one important concept about how the
control works.

Fig 20.14 Orientations and arrows of a SpinButton control

On a UserForm, a SpinButton can be placed with either a vertical or horizontal


orientation. Refer figure 20.14
When a user clicks on the Up Arrow/Right Arrow, the value of the control
moves towards the value of Max property
When a user clicks on the Down Arrow/Left Arrow, the value of the control
moves towards the value of Min property.
Here is a list of the properties that we should set before we can start using the
control. The properties are marked in figure 20.14.a.
Note that we are accessing these properties from the Properties window and
not in macro.
a) Max: This is the limit towards which the Value property of the control will
move when the ‘Up Arrow’ is clicked. The datatype of the property is Long
Integer. This value need not be greater than the Min value.

b) Min: This is the limit towards which the Value property of the control will
move when the ‘Down Arrow’ is clicked. The datatype of the property is
Long Integer. This value need not be smaller than the Max value.

c) Small change: The amount by which value of the control will change when
the user clicks once on one of the arrows.

d) Orientation: Value of this property decides whether the SpinButton will be
shown horizontally (arrows pointing left & right) or vertically (arrows
pointing up & down). The value has to be selected from a dropdown in the
Properties window. However, it is best if the orientation is done when placing
the control on the userform

Fractional and Negative values
To obtain a fractional Value out of a SpinButton, please refer to techniques used
in section 19.11.1. There we have applied the principles to a ScrollBar placed on
a worksheet. The same principles can be applied for a SpinButton on a
UserForm.

Fig. 20.14a Properties of a SpinButton

Notes on Max, Min and Small change properties:


· Highest allowed value for Max/Min is +2.14Billion (that’s right ‘B’) and the
lowest allowed value is -2.14Billion.

· Any or all of these properties can be negative.

· Notice that in figure 20.14a the value of Max is negative and that for Min is
positive.

Using negative value for SmallChange will reverse the functioning of the
arrows. So clicking the Up/Right arrow will move Value property towards Min
and clicking the Down/Left arrow will move Value property towards Max
Usin SpinButton in Macros


Apart from the Enabled and Visible properties described in section 20.6, we
frequently have to use the Max, Min and Value properties in our macros. We
should not to set the values of these properties in our macro. We should only
read the values.
Following code shows how the three values can be used. This code is only for
illustration because it would show a message every time the spinbutton is clicked
hence changing the value and triggering the ‘Change’ event.
Private Sub SpinButton1_Change()
Dim maxval, currval As Double
If SpinButton1.Min > SpinButton1.Max Then
maxval = SpinButton1.Min
Else
maxval = SpinButton1.Max
End If
currval = ((maxval - SpinButton1.Value)/maxval) * 100
MsgBox "Current value is " & _
Format(currval, "0.00") & _
"% less than the maximum"
End Sub

20.15 ScrollBar
ScrollBar control can be placed on a worksheet
by clicking on the icon on the ToolBox palette as
marked in the figure shown on the left.
A scroll bar can be placed either in horizontal or
vertical orientation. The main parts of a ScrollBar
are shown in figure 20.15.
ScrollBox: The part of a
ScrollBar control that the
user can drag towards one
of the ends of the scrollbar
to change the scrollbar’s
value.

Fig 20.15 A Vertical & Horizontal ScrollBars and their parts



Apart from sliding the ScrollBox, value can also be changed by:
· Clicking on one of the Scroll arrows. (The amount of change is determined
by the value of the ‘SmallChange’ property)
· Clicking between the Scroll box and one of the scroll arrows. (The amount of
change is determined by the value of the ‘LargeChange’ property)
The main properties (Max, Min, Orientation, Small Change) of the ScrollBar
control are same as those for a SpinButton and carry the same meaning and can
be set in the properties window in exactly the same way as for a SpinButton (see
section 20.14).
One other property is the LargeChange property. This is a long integer value that
specifieds the amount by which the Value property changes when the user clicks
the area between the scroll box and scroll arrow. The direction of the movement
is always toward the place where the user clicks.
Using a ScrollBar in Macros
Again we would point to the similarity with a SpinButton. The three important
properties are Min, Max and Value properties. Please refer to the code shown in
section 20.14 on how these properties can be used. The only difference being
that instead of the name of the SpinButton (SpinButton1), now we need to use
the name of the ScrollBar (ScrollBar1).
20.16 TextBox
TextBox is a control that provides a way for the user to type information directly
into the control. TextBox control can be placed on a worksheet by clicking on
the icon marked in the following figure.

Name and Common properties of the control can be set using methods described
in section 20.2. The important properties that can be set in the properties window
are shown the figure 20.16a.

Fig 20.16a Properties of a TextBox

We can set the Font of the text in the box and also if the text box allows multi
line text entry.
If “MultiLine” property is set to TRUE, user is allowed to enter paragraphs of
text instead of a single line or word.
The value entered by the user is always available as a string. If we are expecting
numeric values (like the TextBox for ‘Cash discount’ and ‘Quantity’), it is
possible that the user can enter text characters. In that case our macro has to first
check that the user has entered appropriate values before processing it further.
The following line of code will print the text entered by the user in the
immediate window:
Dim strTxt As String
strTxt = TextBox1.Text
Debug.Print strTxt
20.17 Label
Label is one control that does not capture data from the user. Instead, it is used to
display data. Label control can be placed on a worksheet by clicking on the icon
marked in the following figure.

Name and Common properties of the control can be set using methods described
in section 20.2.
An important property is “Caption” which is the text that is displayed on the
label. Usually labels are kept next to controls to display static text. For example,
figure 20.18 shows several labrels used to display static text.

Fig. 20.17 Labels for displaying static text

However, the real advantage of a label is in displaying dynamic values that are
either calculated in the macro code or taken from value of some other control.
The code below is written inside the change event of a scrollbar and it sets the
caption to the latest value of the scrollbar whenever user changes the value of the
scrollbar.
Private Sub ScrollBar1_Change()

Label5.Caption = ScrollBar1.Value
End Sub
Chapter 21
Middleware – The Bridge
In this section we will see how to tie our forms and database together mainly
using code.
This code will be independent of how the form is created (on a worksheet or on a
UserForm). We just have to take care of the connecting points. Where a
distinction has to be made, it will be pointed out explicitly.
21.1 Converting Form data to database values
On a form, we provide data that can be understood by the user, like the name of
the customer or item to be entered. However, the control provides us a different
value, for example the position of the value in the list of values. Or an integer
value or a Boolean value showing whether an option was selected by the user.
Now the data we want to store in the database is different from these two. For
example, we will be storing the IDs of the customer and the items in our
database. Why? Well because when we generate reports we can fetch the name
and other details of items and customers from the MasterData reference.
But then why not store the customer details directly? Because at a later date we
may want to change some thing like the spelling of the customer’s organization.
If we have taken several different orders, at how many places do we make a
change?
An ID is something that is not visible to the human users, so for the same ID, we
can keep changing the text and numeric details. That is why, when recording
transactions, it is better to store the customer ID. However, we have no use for
the position of the item as presented on the form.
For these reasons we have to convert the value provided by the form to the value
fit to be stored. In this section we will see some techniques for achieving this
conversion. First let’s look at forms created on worksheets.

21.1.1 When using a form on a worksheet


In chapter 19, we have already seen how we can do this when controls are placed
on a worksheet. We can use Excel functions INDEX, VLOOKUP, MATCH and
so on.
For our application, we have a separate group of cells in which the values of
controls are used to calculate and derive the required values from the reference
database.
Figure 21.1.1a shows the values for a particular order that are stored in different
cells. These values remain same for any number of items that are added.
ControlValue: Value that is provided by the controls on the form. This may not
be the value we want to store in the Database.
DBValue: Value that is actually stored in the database tables on SalesDB
worksheet.

Fig 21.1.1a Order specific details.

Figure 21.1.1b shows the values that are stored in cells when user adds the items
one at a time. The formulas/functions used are also shown.

Fig 21.1.1b Item specific details.

When the user clicks “Add Item to List” button, a macro will copy the values in
cells BA2 to BA4 to the row below the last row in the columns BG to BI shown
in figure 21.1.1c. This is the range where the order will be compiled.

Fig 21.1.1c Compiled order

Let us look at the code that compiles the order. The code is written in the ‘Click’
event of the button labelled “Add Item to List”.
However, do remember that the same button has to allow addition of single as
well as multiple items. We will build the code for both these cases.
First let us make some arrangements for allowing addition of multiple items.
First we need a way to transfer items between the ListBoxes.
We should also keep in mind that the user might want to Add and Remove items
several times before clicking the “Add Items to List” button. We need a way to
store the selected items such that addition and deletion can be done easily.


Fig 21.1.1d Section of the form that allows adding multiple items

In figure 21.1.1d, the ListBox on the left is a list of all items that are available
for sale.
When user selects one or more items in the ListBox on the left hand side and
clicks on the “Add” button, the selected items get added to the ListBox on the
left.
The two ListBoxes and the buttons between them are initially hidden. The user
can add only one item at a time. To add multiple items, the user has to put a
checkmark in the checkbox shown in figure 21.1.1e.

Fig21.1.1e Checkbox to enable addition of multiple items.

When this box is checked, the ListBoxes and the related buttons are made
visible.
Name of the checkbox is ’chkAddMultiItems’. Here is the code for the click
event of the CheckBox.
Sub chkAddMultiItems_click()
With ThisWorkbook.Worksheets("OrderForm")
If .CheckBoxes("chkAddMultiItems").Value < 0 Then
.DropDowns("cbProdList").Visible = True
.ListBoxes("lbProdList").Visible = False
.ListBoxes("lbSelList").Visible = False
.Buttons("btnAddSelected").Visible = False
.Buttons("btnAddAll").Visible = False
.Buttons("btnRemoveSel").Visible = False
.Buttons("btnRemoveAll").Visible = False
End If
If .CheckBoxes("chkAddMultiItems").Value = 1 Then
.Range("R6").Value = 0
.DropDowns("cbProdList").Visible = False
.ListBoxes("lbProdList").Visible = True
.ListBoxes("lbSelList").Visible = True
.Buttons("btnAddSelected").Visible = True
.Buttons("btnAddAll").Visible = True
.Buttons("btnRemoveSel").Visible = True
.Buttons("btnRemoveAll").Visible = True
End If
End With
End Sub

Let us write the code for the Add and Remove buttons:
1) Adding items from the available products ListBox
When user clicks on the Add button, our macro checks the ‘Selected’ array for
the ListBox on the left side.
Fig 21.1.1f

The for each selected item, the value of the product ID is fetched and stored in
column BM on the same sheet in the last blank row. As shown in figure
21.1.1f. Text of the Selected item is added to the ListBox on the right hand
side.

At this time, the position of the ID in column BM will match the position of the
item in the right side ListBox
This is an important fact that we will use when writing code for the Remove
button.
Here is the sub routine that will be added to the Click event of the ‘Add’
button.



Sub AddMultiItemToSelList()
Dim wks As Worksheet
Dim lbProdList, lbSelList As ListBox
Dim startPos, endPos, rwlastMultiItemID As Long
Dim strProdID As String
Dim rngFound As Range
Set wks = ThisWorkbook.Worksheets("OrderForm")
Set lbProdList = wks.ListBoxes("lbProdList")
Set lbSelList = wks.ListBoxes("lbSelList")

'//** First row contains column headings. **//
‘//** If no items have been added yet, start at the second row **//
rwlastMultiItemID = Application.WorksheetFunction.CountA( _
wks.Columns(colMultiItemID)) + 1

startPos = LBound(lbProdList.Selected)
endPos = UBound(lbProdList.Selected)
For i = startPos To endPos Step 1
If (lbProdList.Selected(i)) Then
'//** Item is selected, get its product ID **//
strProdID = ThisWorkbook.Names("ProdDB").RefersToRange. _
Cells(i, 1).Value
'//** Check if the ID is already added to the column BM **//
Set rngFound = wks.Columns(colMultiItemID).Find(strProdID)
If (rngFound Is Nothing) Then
'//** ID is not added, add it to the column BM **//
wks.Cells(rwlastMultiItemID, colMultiItemID).Value _
=strProdID
'//** Increase the row number as there might be more items **//
rwlastMultiItemID = rwlastMultiItemID + 1
'//** Add the text of the item into the selected items ListBox **//
lbSelList.AddItem (lbProdList.List(i))
'//** Deselect the item since it is now added **//
lbProdList.Selected(i) = False
Else
'//** ID of the selected product is already added, no action required **//
Set rngFound = Nothing
End If
End If
Next i

'//** Cleanup code **//
Set rngFound = Nothing
Set lbProdList = Nothing
Set lbSelList = Nothing
Set wks = Nothing
End Sub

2) Removing items from the Selected Items ListBox
When user clicks on the Remove button, our macro takes all the IDs from
column BM into a Visual Basic array (except for the first row) and clears all
the rows.
Then the macro checks the ‘Selected’ array for the ListBox on the right side.
For each selected item, the value in the Visual Basic array is set to null. We
can do this because the positions in the ‘Selected’ array of the ListBox and the
Visual Basic array holding product IDs matches.
Each item that is not selected is copied to column BM in the bottommost
blank row.
We then loop over the ‘Selected’ array once again and this time remove the
items that are selected.
Here is the sub routine for the Click event of the ‘Remove’ button.
Sub RemoveMultiItemFromSelList()
Dim wks As Worksheet
Dim lbSelList As ListBox
Dim startPos, endPos, rwlastMultiItemID, lngSelIndex As Long
Dim strProdID As String
Dim rngAdded As Range
Dim arrAddedId As Variant
Set wks = ThisWorkbook.Worksheets("OrderForm")
Set lbSelList = wks.ListBoxes("lbSelList")
rwlastMultiItemID = Application.WorksheetFunction._
CountA(wks.Columns(colMultiItemID))
If rwlastMultiItemID < 2 Then
rwlastMultiItemID = 2
End If
Set rngAdded = wks.Range(wks.Cells(2, colMultiItemID), _
wks.Cells(rwlastMultiItemID, colMultiItemID))
arrAddedId = rngAdded.Value
rngAdded.ClearContents
rwlastMultiItemID = 2
startPos = LBound(arrAddedId, 1)
endPos = UBound(arrAddedId, 1)
lngSelIndex = LBound(lbSelList.Selected)
For r = startPos To endPos Step 1
If Not (lbSelList.Selected(lngSelIndex)) Then
'//** Item is not selected for removal, **//
‘//** add it back to the column **//
wks.Cells(rwlastMultiItemID, colMultiItemID).Value = _
arrAddedId(r, 1)
'//** Increment the row counter so that **//
‘//** the next addition happens in the next row **//
rwlastMultiItemID = rwlastMultiItemID + 1
End If
lngSelIndex = lngSelIndex + 1
Next r
'//** Actual deletion from the listbox **//
lngSelIndex = LBound(lbSelList.Selected)
Dim bKeepLooping As Boolean
bKeepLooping = True
While (bKeepLooping)
If lngSelIndex > UBound(lbSelList.Selected) Then
bKeepLooping = False
Else
If (lbSelList.Selected(lngSelIndex)) Then
'//** If the current item is selected, remove it from the ListBox **//
'//** Do not increment the selected index as the next item may be selected.
**//
'//** That item will become the current item in the next iteration **//

lbSelList.RemoveItem (lngSelIndex)
If lbSelList.ListCount < 1 Then
'//** If all items have been removed, stop looping **//
bKeepLooping = False
End If
Else
'//** If the current item is not selected, increment the selected index **//
lngSelIndex = lngSelIndex + 1
End If
End If
Wend
Set lbSelList = Nothing
Set wks = Nothing
End Sub

For the reader: Please write the code for ‘Add All’ and ‘Remove All’
buttons.

3) Adding items to the compiled order
Once the single or multiple item(s) to be included in the order have been
selected, the user will set the value of Special discount using the SpinButton.
Then the user will click on the “Add Item to List” button. Here is the sub
routine that will be assigned to the ‘Click’ event of that button.
Sub AddItemsToList()
Dim wks As Worksheet
Dim chkAddMultiItems As CheckBox
Dim rwlastCompiledOrder As Integer
Dim rwlastItemDetail As Integer
Set wks = ThisWorkbook.Worksheets("OrderForm")
rwlastCompiledOrder = Application.WorksheetFunction.CountA _
(wks.Columns(colCompiledOrderStart)) + 1
Set chkAddMultiItems = wks.CheckBoxes("chkAddMultiItems")
If chkAddMultiItems.Value < 0 Then
'//Single item has to be added
Dim colSingleItemDetail As Integer
colSingleItemDetail = 53
rwlastItemDetail = Application.WorksheetFunction.CountA _
(wks.Columns(colSingleItemDetail))
For Count = 0 To (rwlastItemDetail - 1) Step 1
'//** Following is one single line of code **//
wks.Cells(rwlastCompiledOrder, _
colCompiledOrderStart + Count).Value = _
wks.Cells(2 + Count, colSingleItemDetail).Value

Next Count
Else
'//** Multiple items have to be added **//
Dim rwlastMultiItemID As Integer
Dim rngFind As Range
Dim strProdID As String
Dim dblStdQty, dblSpecDiscount As Double

'//** Note down the value of Special Discount **//
dblSpecDiscount = wks.Range("X6").Value
'//** Get the last row in the column that stores product IDs **//
rwlastMultiItemID = Application.WorksheetFunction._
CountA(wks.Columns(colMultiItemID))
While (rwlastMultiItemID > 1)
'//** For each item listed in the column **//
strProdID = _
wks.Cells(rwlastMultiItemID, colMultiItemID).Value
'//** Find this ID in the first column of the products table. **//
Set rngFind = ThisWorkbook.Names("ProdDB"). _
RefersToRange.Columns(1).Find(strProdID)
dblStdQty = rngFind.Offset(0, 4).Value
With wks.Cells(rwlastCompiledOrder, colCompiledOrderStart)
.Value = strProdID
.Offset(0,1).Value = dblStdQty
.Offset(0,2).Value = dblSpecDiscount
End With
wks.Cells(rwlastMultiItemID,colMultiItemID).ClearContents
rwlastCompiledOrder = rwlastCompiledOrder + 1
rwlastMultiItemID = rwlastMultiItemID - 1
Wend

wks.ListBoxes("lbSelList").RemoveAllItems
End If
Set chkAddMultiItems = Nothing
End Sub

For the reader:
Can you add the code to check that if the user has opted to add multiple items,
then atleast one item is present in the right hand side ListBox?
If no item is selected, then display a message to the user and exit from the
procedure.

4) Saving the compiled order
Once the order is compiled, the user will set the value of ‘Cash Discount’
using a ScrollBar and select the ‘Sales Region’ using one of the
OptionButtons.
Then the user will click on the ‘Save Complete Order’ button which will do
the following:
A) Append item details in columns A to D of worksheet named ‘SalesDB’


B) Append order details in column H to L of worksheet named ‘SalesDB’
This mainly involves copying the items from one sheet to another and finally
preparing the form for entering the next order.

Sub SaveCompiledOrder()
Dim wksDB, wksOrder As Worksheet
Dim rwlastSavedItem, rwlastCompiledOrde As Long
Dim rngTemp As Range
Dim colSalesDBStart, colOrderDBStart As Integer
Dim strOID, strCID As String
colSalesDBStart = 1
colOrderDBStart = 8
Set wksOrder = ThisWorkbook.Worksheets("OrderForm")
Set wksDB = ThisWorkbook.Worksheets("SalesDB")

rwlastSavedItem = Application.WorksheetFunction. _
CountA(wksDB.Columns(colSalesDBStart)) + 1

'//** Last row of the compiled order **//
rwlastCompiledOrder = Application.WorksheetFunction. –
CountA(wksOrder.Columns(colCompiledOrderStart))

Set rngTemp = wksOrder.Cells(2, colCompiledOrderStart). _
Resize(rwlastCompiledOrder - 1, 3)
rngTemp.Copy (wksDB.Cells(rwlastSavedItem, 2))
rngTemp.ClearContents
Set rngTemp = Nothing
Set rngTemp = wksDB.Cells(rwlastSavedItem, 1).Resize _
(rwlastCompiledOrder - 1, 1)
wksOrder.Range("BE6").Copy
rngTemp.PasteSpecial (xlPasteValues)
Application.CutCopyMode = False

'//** Storing order details **//
rwlastSavedItem = Application.WorksheetFunction.CountA _
(wksDB.Columns(colOrderDBStart)) + 1

With wksDB.Cells(rwlastSavedItem, colOrderDBStart)
wksOrder.Range("BE6").Copy
.PasteSpecial (xlPasteValues)
Application.CutCopyMode = False
.Offset(0, 1).Value = wksOrder.Range("BE3").Value
.Offset(0, 2).Value = wksOrder.Range("BE2").Value
.Offset(0, 3).Value = wksOrder.Range("BE5").Value
.Offset(0, 4).Value = wksOrder.Range("BE4").Value
.Offset(0, 5).Value = _
Trim(Left(wksOrder.Range("C21").Value, 300))
End With
'//** Store latest order ID for reference in next order **//
ThisWorkbook.Worksheets("MasterData").Range("J2").Value = _
wksOrder.Range("BE6").Value
'//** Restore controls to default value **//
Call ResetControls
End Sub

Here is the code to restore the controls to their default values. We have added
this is as another sub-procedure. The reason being we would need this when
writing the code for the ‘Cancel’ button.

Sub ResetControls()
Dim wksOrder As Worksheet

Set wksOrder = ThisWorkbook.Worksheets("OrderForm")

With ThisWorkbook.Worksheets("OrderForm")
.CheckBoxes("chkAddMultiItems").Value = -4146
.CheckBoxes("chkCashDiscount").Value = -4146
.CheckBoxes("chkSpecDiscount").Value = -4146
.Spinners("spbtnSpecDiscount").Value = _
.Spinners("spbtnSpecDiscount").Min

.ScrollBars("scrlCashDiscount").Value = _
.ScrollBars("scrlCashDiscount").Min
.DropDowns("cbProdList").Visible = True
.ListBoxes("lbProdList").Visible = False
.ListBoxes("lbSelList").Visible = False
.Buttons("btnAddSelected").Visible = False
.Buttons("btnAddAll").Visible = False
.Buttons("btnRemoveSel").Visible = False
.Buttons("btnRemoveAll").Visible = False
End With
End Sub

For the reader:


Please write code for the ‘Cancel’ button. Clear all the relevant ranges and
then call the ResetControls sub procedure.
Ensure that none of the cells containing formulas are affected.

21.1.2 When using a VB UserForm


The basic principles for converting data from controls to values for database are
the same in the Visual Basic UserForms as for the forms created on worksheets.
We will just highlight the main differences
For worksheet based forms, the values from controls are stored in worksheet
cells, while for VB UserForms, the values are available in the Value property of
controls and have to be captured in variables.
For controls like ScrollBar and SpinButton, on a worksheet, the value is visible
to the user in a cell. For a UserForm we would have to write code to take the
value and update value of some other control like a Label or a TextBox.
The following code will show the lastest value of the ScrollBar in the TextBox
on our form.

Private Sub scrlCashDiscount_Change()
If chkCashDiscount Then
txtCashDiscount.Value = scrlCashDiscount.Value
End If
End Sub
21.2 Form Validation
Validation deals with correctness of the format of the data entered in the form. it
is not concerned with the correctness of value of data.
For example, in a TextBox where the user has to enter his email. validation part
of our software will check the flowing
1. there is at least one ( . )
2. there is only one @
3. there are no invalid characters like :, ; %,#,?,!,*,{,} and so on
Often validation also involves checking that number or dates are within certain
range or that the user has actually entered a number and not text where a number
was required.
In most cases, the very use of controls ensures that the data is valid, that is their
purpose. For example, the spinner and scrollbar control ensure that only numeric
values in certain range are entered, the option buttons, combo/list boxes ensure
that text values only from a certain group are entered.
There are however cases where we have to take special efforts to validate data.
we will see two such cases.
1) TextBox
In case of a textbox, the value entered by the user is available to our macros as a
string. The validity of the data can be checked using the functions described in
section 8.2 “Functions for Strings”. For example:
· To check the presence or absence of characters we can use the ‘InStr’
function
· To check if the user has entered a number, we can use the ‘IsNumeric’
function.
· Once it is confirmed that user has entered a number, we can get the numeric
value using the ‘Val’ function
2) When values of controls are interdependent
In such cases, the values selected by the user in the individual controls are within
the allowable range, but the combination of values violates some business rule.
For example, suppose we are not allowed to give the customer a special discount
for Steel Pipe if the quantity ordered is below 100. If the user entering the order
enters the following

We can write a function that can check the rule and then return a Boolean (True)
if the values are valid and False if the rule is violated. Here is the code for the
function
FunctionIsFormValid() As Boolean
Dim wks As Worksheet
Dim chkAddMultiItems As CheckBox
Dim rwlastCompiledOrder As Integer
Dim rwlastItemDetail As Integer
Set wks = ThisWorkbook.Worksheets("OrderForm")
Set chkAddMultiItems = wks.CheckBoxes("chkAddMultiItems")
If chkAddMultiItems.Value < 0 Then
'//** Single item has to be added **//
Dim colSingleItemDetail As Integer
colSingleItemDetail = 53

'//** If block to check the values **//
If _
wks.Cells(2,colSingleItemDetail).Value = “PP001AFD” And _
wks.Cells(3,colSingleItemDetail).Value <100 And _
wks.Cells(4,colSingleItemDetail).Value <> 0 Then
'//** The rules are violated, return False and exit function **//
IsFormValid = False
Exit Function
End If
Else
'//** Multiple items have to be added **//
Dim rwlastMultiItemID As Integer
Dim rngFind As Range
Dim strProdID As String
Dim dblStdQty, dblSpecDiscount As Double
Dim colMultiItemID As Integer
colMultiItemID = 65

'//Note down the value of Special Discount
dblSpecDiscount = wks.Range("X6").Value
'//Get the last row in the column that stores product IDs
rwlastMultiItemID = Application.WorksheetFunction._
CountA(wks.Columns(colMultiItemID))
While (rwlastMultiItemID > 1)
'//For each item listed in the column
strProdID = wks.Cells(rwlastMultiItemID, _
colMultiItemID).Value

'//Find this ID in the first column of the products table.
Set rngFind = ThisWorkbook.Names("ProdDB"). _
RefersToRange.Columns(1).Find(strProdID)
dblStdQty = rngFind.Offset(0, 4).Value
If (strProdID = “PP001AFD” And dblStdQty<100 And _
dblSpecDiscount<> 0) Then
IsFormValid = False
Exit Function
End If
rwlastMultiItemID = rwlastMultiItemID - 1
Wend
End If
Set chkAddMultiItems = Nothing

'//If the function is not exited so far, it means the values are valid. Return True.
IsFormValid = True
End Function


Then in the click event of “Add Item to List” button (similar to the
AddItemsToList sub that we created in section 21.1.1) we should call this
function and exit the sub if the returned value is False.
Sub AddItemsToList()
Dim strValidationMsg as String

strValidationMsg = _
“Special Discount can’t be applied to Steel” & _
“for Quantity below 100” & VbCrLf & _
“Please cancel and re-enter the order”

If Not(IsFormValid) Then
MsgBox strValidationMsg
End Sub
End If
'//Remaining code of the sub
If chkAddMultiItems.Value < 0 Then…..
'//Remaining code of the sub
End Sub


21.3 The SwitchBoard Form
Switchboard form is sort of a menu from where the user can launch different
functions of the application. This is provided so that the users can perform
different tasks as per their convenience and not necessarily in a sequence. For
example, the user might want to enter two orders one after the other and then
generate the invoices for the orders at a later date.
Switchboard is usually created on a worksheet and usually it is the first form
displayed to the user when the application is launched.
As for controls, a switchboard mainly contains only buttons and checkboxes
since too much data entry is not expected. The switchboard for our application is
shown in the figure 21.3. There is a new term on the form ‘Picklist’. It is actually
an output that we can generate from data stored in our database.
A picklist is a list of items and related quantities to be delivered for a particular
order. It is provided to the employees in the warehouse so that they can pick out
the items and pack them for delivery.
The Invoice and the Picklist are generated for a specific order that has already
been entered. Hence a user would have to specify the related order ID.

Fig 21.3 Switchboard form for our application


The OrderID can be provided in a drop down for selection. However, in our
illustration we assume that the user has to enter it manually in cell G7 of a
worksheet named “Switchboard”
21.4 Creating outputs
Now that we have seen how we can enter and store data into our application,
let’s see how we can extract different types of outputs from the stored data. To
illustrate the principles, we will look at two different reports that would require
us to perform calculations, copy data, insert formulas on worksheet and create
charts.
The matter in this section uses many of the techniques shown in chapters in the
earlier part of the book so the reader may have to refer to other chapters for
clarification

21.4.1 Invoice
Once we have entered an order, we may want to generate an invoice that will be
sent to the customer. We will write a macro that will take the OrderID entered by
the user and creates the invoice for that order.
The figure 21.4.1 shows the invoice that will be created. The report is formatted
and sized so as to fit on one standard A4 sized sheet of paper. This is a blank
template and actual invoice will be created in a copy of this worksheet.
Note: In the figure, some of the rows between 18 to 38 and between 44 to 49
have been hidden in order to save space.
Fig. 21.4.1 Template for invoice

One sheet (of pater) can have a list of 20 items. If the number of items exceeds
20, we will create another copy of the template sheet. The multiple worksheets
of the invoice will be stored in a separate file.
Here is how the macro will work.
First details of customer and order will be filled on the template sheet this is
done so that if more than one copy of the template is required we won't have to
fill in the details repeatedly.
A new workbook will be created. A copy of the invoice template will be placed
in the new workbook. Let us call this the ‘Invoice sheet’.
Based on the order ID entered by the user, rows will be filtered from ‘SalesDB’.
these will be copied in the switchboard form in an area that is out of view. We
can use any other worksheet for this purpose because this is only a temporay
holding place.
Starting at the first row of copied data, items are entered in the ‘Invoice sheet’.
The price (after special discount) is calculated and the value is added to the
running sum of the total invoice value.
After the twentieth item is added, and if there are more items remaining, a new
copy of the invoice template is added to the new workbook.
Once all items are added, the new workbook is saved with the name
<ordered>_invoice.xlsx
The customer and order details are removed from the original invoice template
sheet so that it can be used for some other order.
Let's see the code. Comments have been provided at appropriate places.


Sub makeInvoice()
Dim strOrderID, CustID As String
Dim rng As Range
Dim rwcount, invrw, itmcount As Long
Dim wks, wksInv, wksSwitch, wksSales As Worksheet
Dim pagecount As Integer
Dim wbkInv As Workbook
Dim strprodID, strprodDesc, strMeasure As String
Dim dblQty, dblPrice, dblSpecDisc, dblInvValue As Double
Set wksSwitch = ThisWorkbook.Worksheets("SwitchBoard")
Set wksSales = ThisWorkbook.Worksheets("SalesDB")

‘//** Get the OrderID entered by the user on SwitchBoard sheet **//
strOrderID = Trim(wksSwitch.Range("G7").Value)

rwcount = Application.WorksheetFunction.CountA _
(wksSales.Columns(1))
Set rng = wksSales.Range("A1").Resize(rwcount, 4)

‘//** Filter the range based on OrderID **//
rng.AutoFilter Field:=1, Criteria1:=strOrderID
rng.SpecialCells(xlCellTypeVisible).Copy

‘//** Copy filtered rows to temporary area on the SwitchBoard **//
wksSwitch.Range("$AA$1").PasteSpecial (xlPasteValues)
rng.AutoFilter
Set rng = Nothing
rwcount = Application.WorksheetFunction.CountA _
(wksSwitch.Columns(28))
invrw = 17
itmcount = 1
dblInvValue = 0
pagecount = 1
'//** Find the customer Id using the order ID **//
Set rng = wksSales.Range("H:H").Find(strOrderID)
CustID = rng.Offset(0, 3).Value
'//** Locate the row of that customer in the CustomerDB **//
Set rng = ThisWorkbook.Names("CustomerDB"). _
RefersToRange.Columns(1).Find(CustID)

'//** Fill up the invoice template sheet with customer and order details. **//
With ThisWorkbook.Worksheets("Invtemp")
.Range("D5").Value = strOrderID
.Range("D5").Value = rng.Offset(0, 1).Value
.Range("B7").Value = rng.Offset(0, 2).Value
.Range("G7").Value = rng.Offset(0, 3).Value
End With

'//** Create a new workbook (Invoice workbook) and **//
‘//** Paste first invoice sheet into it just before the first sheet **//
Set wbkInv = Workbooks.Add
ThisWorkbook.Worksheets("Invtemp").Copy _
Before:=wbkInv.Worksheets(1)
Set wksInv = ActiveSheet
wksInv.Name = "Page_" & Format(pagecount, "00")
For r = 2 To rwcount Step 1
strprodID = ""
dblSpecDisc = 0
dblQty = 0
dblPrice = 0
With wksInv
strprodID = wksSwitch.Cells(r, 28).Value
dblSpecDisc = wksSwitch.Cells(r, 30).Value
dblQty = wksSwitch.Cells(r, 29).Value
Set rng = ThisWorkbook.Names("ProdDB"). _ RefersToRange.Columns(1).Find(strprodID)

dblPrice = rng.Offset(0, 2).Value
strprodDesc = rng.Offset(0, 1).Value
strMeasure = rng.Offset(0, 3).Value

'//** Set the values in invoice **//
.Cells(invrw, 2).Value = itmcount
.Cells(invrw, 3).Value = strprodDesc
.Cells(invrw, 7).Value = dblPrice
.Cells(invrw, 8).Value = dblSpecDisc
dblPrice = dblPrice * (100 - dblSpecDisc) / 100
.Cells(invrw, 9).Value = dblPrice
.Cells(invrw, 10).Value = dblQty & " " & strMeasure
.Cells(invrw, 11).Value = dblPrice * dblQty
dblInvValue = dblInvValue + dblPrice * dblQty

If (itmcount Mod 20) = 0 Then
'//** For every 20 items, paste a new copy of the Invoice template **//
'//** Make the new sheet the active sheet that will receive the input **//
'//** Name the new sheet as per the next page number **//
ThisWorkbook.Worksheets("Invtemp").Copy After:=wksInv
Set wksInv = ActiveSheet
pagecount = pagecount + 1
wksInv.Name = "Page_" & Format(pagecount, "00")
invrw = 17
Else
invrw = invrw + 1
End If
itmcount = itmcount + 1
Set rng = Nothing
End With
Next r

'//** Insert the total value of the invoice in the last sheet **//
wksInv.Range("J39").Value = dblInvValue

'//** Display the first sheet in the Invoice workbook, then save and close the workbook **//
wbkInv.Worksheets("Page_01").Activate
wbkInv.SaveAs ("C:\reports\" & strOrderID & "_Invoice.xlsx")
wbkInv.Close

'//** Clear all the cells on the Switchboard and Invoice template worksheets **//
wksSwitch.Range("AA:AD").ClearContents
With ThisWorkbook.Worksheets("Invtemp")
.Range("D3").ClearContents
.Range("D5").ClearContents
.Range("B7").ClearContents
.Range("G7").ClearContents
End With
End Sub

21.4.2 Top-5 components by volume


Management would like to have a report showing the Top selling 5 components
in each region for the past 3 months. The report is required as a pie chart. We
have to create one pie chart for each region.
Initial setup
To set-up our report for automation we need to create it manually at least once.
A) Work out the start and end dates
The latest month will be called “near” month, the earliest (farthest/earliest)
month will be called “far” month
If in the current month we are beyond 25thday, the current month will be
called near month. The far month will be the month before the previous
month.
If in the current month we are before the 25th, the current month data will not
be considered. In this case the previous month will be the near month and the
far month will be the month before the previous two months.

B) Notice that in the ‘SalesDB’ table we have one blank column to the right. In
this column we will lookup the order dates for each component. See how we
have linked the SalesDB and the OrderDB using the OrderId as the key.

Fig 21.4.2a

Figure shows the table on SalesDB worksheet with the formula applied in
column E
C) Apply a filter and take out the rows that fall between the start and end dates.
D) Transfer the filtered rows to Column A of a worksheet named “StageSheet”.
Notice that we are not going to transfer the dates that we looked up.
E) Now, only for this small set of rows, look up the sales region from the
orderdb again using OrderId as the key. Convert the formulas to values.

Fig 21.4.2b

Figure shows the table on StageSheet worksheet with the formula applied in
column E
F) Next, create a pivot table with components in rows and region in columns.
Values will be the sum of sales quantities. This pivot table will be on the
staging sheet.

Fig 21.4.2c Pivot table for units sold by region

Notice that we are not displaying the totals. In step H we will copy the cells from
H2:L10.
G) Copy the names of components (first col. Of pivot table) to col O on the
staging sheet.
H) Copy the total units sold FOR ONLY ONE REGION to column Q.
I) Sort the columns O to Q in descending order of unitsnsold.
J) Remove any rows where the units sold are 0 or blank.
K) Remove any non blank rows beyond five components.
L) In col. P lookup the names of the components from the ProdDb table (Notice
again how two tables are linked to provide only the required information).
M) For the remaining rows, create a pie chart showing the values in percentage.
Only columns P and Q should be included.
Fig 21.4.2d The data and pie chart for one region

N) This chart is for just one region. Save it as an image so that later it can be
transferred to a PowerPoint presentation file.
O) Repeat steps H to L for other regions. The.pie chart will get updated each
time.
Notice the following: In some regions we may be selling only 2 or 3 products. In
that case the chart will be based on only those rows. In case of five or more
components we need to use only the first five rows.
The best way to deal with this changing data, is to create an expanding named
range. We will create two ranges. One for the component names and other for
the number of units. With reference to figure 21.4.2d, the formulas for the ranges
are as follows:
Top5CompName

=OFFSET(StageSheet!$O$1,1,1,COUNTA(StageSheet!$Q:$Q)-1,1)

Top5CompVal

=OFFSET(StageSheet!$O$1,1,2,COUNTA(StageSheet!$Q:$Q)-1,1)

Now we set our chart to refer to that range. See section 10.4 “Using dynamic
ranges in chart series” for the steps to set a chart series with a named range.
Next time when the report has to be produced, our macro will perform the
following steps:
1) Refresh the data in columns A to E on the staging sheet by following steps A
to E described earlier.
2) Set the range of the pivot table to the new data and refresh the pivot table.
3) Repeat steps G through O.
Now we can start coding our macro. Read through the code and see how the
cells on the worksheets would change. Comments are provided at appropriate
places.
Sub Top5ComponentsReport()
‘//** Declaring the variables **//
Dim endDate, startDate As Date
Dim colOrderDate, colOrderRegion, c As Integer
Dim strOrderDateFormula, strOrderRegionFormula As String
Dim strCompNameFormula, strcrit1, strcrit2, strAddr As String
Dim rcount As Long
Dim rngtemp As Range
Dim pvt As PivotTable
Dim wksStage As Worksheet

colOrderDate = 5

colOrderRegion = 5

'//** Compile all the formula strings **//
strOrderDateFormula = "=vlookup(a2,H:M,5,0)"
strOrderRegionFormula = "=vlookup(a2,SalesDB!H:M,2,0)"
strCompNameFormula = "=vlookup(O2,ProdDB,2,0)"
Set wksStage = ThisWorkbook.Worksheets("StageSheet")
wksStage.Range("A:E").ClearContents

'//** Get the start and end dates **//
If Day(Date) < 25 Then
endDate = DateSerial(Year(Date), Month(Date), 1)
endDate = DateAdd("d", -1, endDate)
startDate = DateAdd("m", -3, endDate)
Else
endDate = Date
startDate = DateAdd("m", -2, endDate)
startDate = DateSerial(Year(startDate), Month(startDate), 1)
End If

strcrit1 = ">" & Format(startDate, "mm/dd/yyyy")

strcrit2 = "<=" & Format(endDate, "mm/dd/yyyy")

With ThisWorkbook.Worksheets("SalesDB")
.Cells(1, colOrderDate).Value = "OrderDate"
'//** Lookup the orderdates in the transactions table on SalesDB worksheet **//
.Cells(2, colOrderDate).Formula = strOrderDateFormula
rcount = Application.WorksheetFunction.CountA(.Columns(1))
.Cells(2, colOrderDate).Resize(rcount - 1, 1).FillDown
'//** Filter based on the order dates **//
Set rngtemp = .Cells(1, 1).Resize(rcount, colOrderDate)
rngtemp.AutoFilter Field:=colOrderDate, _
Criteria1:=strcrit1, Operator:=xlAnd, _
Criteria2:=strcrit2
Set rngtemp = Nothing
'//** Copy filtered cells to Staging sheet **//
Set rngtemp = .Cells(1, 1).Resize(rcount, colOrderDate - 1)

rngtemp.SpecialCells(xlCellTypeVisible).Copy _
(wksStage.Cells(1, 1))
'//** Remove filter **//
.Cells(1, 1).Resize(rcount, colOrderDate).AutoFilter
Set rngtemp = Nothing
End With

With wksStage
.Cells(1, colOrderRegion).Value = "Region"
'//** Lookup order region **//
.Cells(2, colOrderRegion).Formula = strOrderRegionFormula
rcount = Application.WorksheetFunction.CountA(.Columns(1))
.Cells(2, colOrderRegion).Resize(rcount - 1, 1).FillDown
.Cells(2, colOrderRegion).Resize(rcount - 1, 1).Copy
.Cells(2, colOrderRegion).PasteSpecial (xlPasteValues)
Set rngtemp = .Cells(1, 1).Resize(rcount, colOrderRegion)
End With

'//** Set the data range for pivot table and refresh **//
Set pvt = wksStage.PivotTables("ProdRegionsalesSummary")
pvt.SourceData = rngtemp.Worksheet.Name & "!" & rngtemp.Address

pvt.RefreshTable

Set rngtemp = Nothing

'//**Get a reference to range of pivot table having values and row and column headings **//
'//** Refer to figure 21.4.2c **//
Set rngtemp = pvt.TableRange2.Cells(2, 1).Resize_
(pvt.TableRange2.Rows.Count - 1,pvt.TableRange2.Columns.Count)

'//** Start looping throught the columns for each of the regions **//
For c = 2 To rngtemp.Columns.Count Step 1
wksStage.Columns("O").ClearContents
wksStage.Columns("Q").ClearContents
rngtemp.Columns(1).Copy(wksStage.Range("O1"))
rngtemp.Columns(c).Copy(wksStage.Range("Q1"))
rcount = rngtemp.Rows.Count – 1

'//** Sort the rows based on the values of units sold in the region **//
With wksStage.Sort
.SortFields.Clear
.SetRange (wksStage.Range("O1:Q" & (rcount + 1)))
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom

.SortFields.Add Key:=wksStage.Range("Q1").Offset(1,0)._
Resize(rcount, 1), SortOn:=SortOnValues, _
Order:=xlAscending
.Apply
End With

'//** Delete all contents beyond the top 5 rows **//
strAddr = "O7:Q" & (rcount + 1)

wksStageRange(strAddr).ClearContents

'//** take the top 5 rows, if any of these has 0 sales values, remove those rows. **//
For r = 6 To 2 Step -1
If wksStage.Cells(r, 17).Value <= 0 Then

wksStage.Range("O" & r & ":Q" & r).ClearContents
End If
Next r

'//** Bring in component names in place of **//
wksStage.Range("P2").Formula = strCompNameFormula

wksStage.Range("P2:P6").FillDown

'//** save the chart picture to a file. Refer to methods described in Chapter 11 **//
Next c
End Sub


Appendix A
Error Handling
In this chapter we will focus on the basic methods available to handle errors that
might occur in our macros.
Please note that our prime focus should be to avoid errors instead of building
ways to handle them. However, error handling is an important part of
programming and in this chapter we will see some general techniques of error
handling. Let’s gets started.
A.1 Some Terminology
Error: An error is any unplanned or unforeseen event that would prevent our
macro from carrying out its intended purpose.
Compile time errors: These are made by the programmer at the time of writing
the macro. These are immediately detected and reported by the Visual Basic
Editor so we can make corrections in the code.
Runtime errors: These errors occur only when our macro is executed. These are
fatal because the macro execution stops abruptly, any objects and variables
ceated and active till that point, are held in memory unless the user accepts to
forcefully stop the macro.
We need to anticipate them and write code that would tell visual basic what
action to take if the error occurs.
Error handler: Block(s) of code that gets executed as a response to a runtime
error. These are written in the same function or sub procedure in which we
expect the error to occur. We will clarify this with examples. However, here are
few points to remember when we use this method:
1. Each block of error handling code should be placed after the last line of our
main code. Even if we have multiple error handlers, they should be placed one
after the other but after our main code.
2. Each error handler should end with ‘Exit Sub’, ‘Resume’ or ‘Resume Next’
statements. This allows control to move back to the main code. Otherwise macro
execution will continue to the immediately next line of code which might belong
to another error handler.
3. Each error handler should be marked with a unique and meaningful label. This
label and the related error handler should be part of the same function or sub
procedure where we expect the error to occur.
4. We should place an ‘Exit Sub’ or ‘Exit Function’ statement immediately
before the label of the first error handler. If not, after executing the main code,
Excel tries to execute the lines in the error handlers even if no error has occured.

Err object: When the execution of our macro begins, Visual Basic automatically
creates an object that would help our code respond to errors. This object is the
‘Err’ object. Every error that occurs at run time has a unique code and this code
is captured in the ‘Number’ property of the Err object.
We can have a ‘Select Case’ or ‘If-Else’ block that can check the ‘Number’
property to determine the type of error that has occurred and then take the
necessary action. If no error occurs the number property has a value zero. We
will not try to learn about all possible error numbers. Instead we will just check
if the number property is non-zero and then check the variables and objects of
our code directly for any errorneous conditions.
In the sections that follow, we will use the phrase “Main Code” to denote the
code that our macro will execute if no error occurs. This is to differentiate it
from code of error handlers. If an error occurs, execution of the main code will
either resume once the error is handled, or the macro execution will stop.
A.2 On Error.... please do something
VBA provides three statements that help us to activate specific error handling
code.
These statements begin with "On Error.." and should be placed before the code
that we expect to cause an error.
The "On Error" statements can be used to transfer control of our macro to a
different section of the procedure or function that contains a different set of lines
of code. This section is called the “Error Handler” for that particular sub or
function procedure. In this section we can either rectify the effects the error or
exit from the procedure or stop the macro execution.
A) On Error Go To 0
This statement disables any error handling in the currently executing
procedure.
Unless we have advanced programming knowledge there is no use in turning
error handling on and off. We will never use this statement.
B) On Error Resume Next
With this statement, execution proceeds to the statement immediately after the
line of code that caused the error. Few things to remember when using this
statement
1. The immediately next line of code should have a block that checks for each
expected error condition.
2. The statement that caused the error is not executed, and it is not possible to
return back to it. So we have to rewrite that code after the error-handling
code.
3. At the end of the error-handling code we must clear out the Err object to
reset its Number property to Zero. Or else some other subsequent code that
checks the Number property may get executed.
4. We should keep the entire error handling code inside an ‘If-Else’ block or
else much of that code will get executed even if an error does not occur.
For example, consider the following line of code:
wbk.Worksheets("Sales").Range("A1"). Value =100/dblDivisor

This statement can cause an error in the following ways:


· The ’wbk’ object might be null or nothing. (Error 91 Object variable or
With block variable not set)
· A worksheet named sales may not exist in the workbook. (Error 9 Subscript
out of range)
· The variable ’dblDivisor’ may be zero. (Error 11 Division by zero)
There are two approaches to write our error-handling:
Ø Check for each error number.
We will avoid this approach as this requires knowing the individual error
numbers. Furthermore, even if we know the error numbers, we should know
which particular object in the code is involved in causing the error.

Ø Just check for a non-zero error number and then check the objects directly.
In this case we will first just check if the number property of the Err object
is non-zero. If it is, we will directly check for the validity of the objects &
variables.
Here is the code (read the comments carefully):
On Error Resume Next
wbk.Worksheets("sales").Range("A1"). Value = 100/dblDivisor
If Err.Number <> 0 Then
‘//** An error has occurred since the Number property is non-zero. **//
If wbk Is Nothing Then
‘//** wbk is not a valid object, set it to the current workbook **//
Set wbk = ThisWorkbook
End If
If dblDivisor = 0 Then
‘//** we tried dividing by zero, set the divisor to default value of 1 **//
dblDivisor = 1
End If

Dim wks As Worksheet

‘//**Try to get a reference to the worksheet named “Sales” **//
‘//** If the worksheet does not exist, an error would occur. However, control will just
pass to **//
‘//** the next line of code due to “On Error Resume Next” **//
Set wks = wbk.Worksheets("Sales")
If wks Is Nothing Then
‘//** The worksheet does not exist, add a new sheet and name it as “Sales” **//
wbk.Worksheets.Add.Name = "Sales"
Else
‘//** The worksheet is already present, release the reference variable. **//
Set wks = Nothing
End If

‘//** Repeat the original line of code **//
wbk.Worksheets("Sales").Range("A1").Value = 100 / dblDivisor
‘//** Error is handled, reset the error number to zero **//
Err.Clear
End If


Notice two things in the code just shown:
· The order of checking the objects/variables is important. The wbk object has
to be checked before we can check for existence of the worksheet. Otherwise,
we will not know which object caused an error in the line

Set wks = wbk.Worksheets("Sales")

· If the entire code was not placed inside the "If Err.Number <> 0 Then" block, the
following lines would get executed even if no error occurred:
Dim wks As Worksheet
Set wks = wbk.Worksheets("Sales")
wbk.Worksheets("Sales").Range("A1").Value = 100 / dblDivisor
Err.Clear

C) On Error GoTo <line label>


If we look at the “On Error Resume Next” statement, we can see that we are
still dependent on the Err object for our error handling. Also, our main code
becomes a bit less readable and maintainable with error handling code placed
right in the middle of it.
These issues can be overcome using the On Error GoTo <line label>
statement. In this case, the error-handling code is separated from the main
macro’s code and marked with a unique label.
This statement sends the control of the macro to a section of code marked with
a label and is best used when the error handling code is quite long and we
don't want to mix it with our main code.
The error handling code is placed towards the end of the main code just before
the ‘End Sub’ or ‘End Function’ statements.
An ‘Exit Sub’ or ‘Exit Function’ statement should be placed right before the
first line label right where the main code ends. This statement will play the
part of ‘End Sub’ or ‘End Function’ statement in case the main code executes
without error.
The actions we can take in the separate section of code are the same that are
available in case of “On Error Resume Next”.
The following code shows the same example used in “On Error Resume Next”
but written differently this time. Read the comments carefully and notice the
presence of ‘Exit Sub’ and multiple ‘Resume’ statements.
Error handler is marked with the label ’ErrHandle’. Notice the colon ( : ) after
the label. The label is placed in a separate line of its own.
Sub DivideNDisplay(ByVal dblDivisor As Double)

'//** Initial code of the Sub Procedure **//

On Error GoTo ErrHandle
wbk.Worksheets("sales").Range("A1").Value = 100 / dblDivisor

'//** Remaining code of the Sub Procedure **//

'//** If no error occurred so far, exit the sub **//
Exit Sub

'//** Error handler begins here, notice the colon after the label name **//
ErrHandle:
If wbk Is Nothing Then

Set wbk = ThisWorkbook
Resume
End If
If dblDivisor = 0 Then

dblDivisor = 1
Resume
End If

'//** Boolean variable to check if a sheet exists **//
Dim bSheetFound As Boolean

'//** Initially assume that the sheet does not exist **//
bSheetFound = False

'//** Check the name of each sheet one by one **//
For i = 1 To wbk.Worksheets.Count Step 1
If StrComp(wbk.Worksheets(i).Name, "Sales") = 0 Then

'//** Sheet already exists, set the Boolean variable to TRUE and exit the loop
**//
bSheetFound = True
Exit For
End If
Next i

'//** If the Boolean variable is false, add another sheet and name it as required **//
If Not (bSheetFound) Then
wbk.Worksheets.Add.Name = "Sales"
End If

'//** Go back to the line that caused the error, execute it again **//
Resume
End Sub

Notice the ’Resume’ statement just before the ’End Sub’ statement. It means
program control will return to the line that caused the error and execute it again.
We can have a ’Resume Next’ statement in which case, program control will go
to the line immediately after the line that caused the error. In this case, the task to
be performed by the line that caused the error should be completed in the error
handler code.
A.3 Avoiding errors
Although visual basic provides ways to handle errors, it is better to anticipate
errors and write code that would check for error conditions.
We list here examples of some common conditions that can lead to errors and
what checks we can do to avoid the errors.
1. The required object does not exist. For example:
· The required file is not present on disk; we have a line of code that tries to
open that file. This results in an error.
The solution – first write code that uses methods like ‘FileExists’ and
‘FolderExists’ to ensure that the file is available. If file doesn’t exist, give an
option to the user to browse to the file location.The code to open the file
should come after these lines.

· The required worksheet does not exist in the workbook. The solution is to
Loop through all the worksheets to ensure that the required worksheet is
available.

2. Value is not of the correct data type.
We read a value from worksheet and assume it is always a number.The code
proceeds to perform mathematical operations on this value.
However, sometimes the value is a piece of text and can cause an error.
The solution - Use the “IsNumeric” function to confirm that the value taken
from the worksheet is actually a number
3. A “While” Loop with no exit condition.
This condition arises very often due to negligience. Please refer to the
discussion on ‘While Loop’ and ‘Infinite Loop’ in section C.9.2.
4. We use a method that may return “Nothing”
Often methods and functions return reference that does not point to any object.
Such a reference is called Null reference or “Nothing”. For example:
The “Find” method of the Range object that is used to find values inside a
range, returns another Range object containing the Cell that contains the value
sought. However, if the value is not found the method returns ‘Nothing’.
Often we write code where we capture the reference returned by “Find” in
another Range object (variable) and proceed to perfrom operations on that
object. If the returned reference is Nothing, an error occurs.
The solution – use an IF-Then block to check that the returned reference is not
‘Nothing’. Only then proceed with other operations. Refer to the code in
section 3.2 “Finding vaues with VBA” for a code example.
5. Using Integer variable for holding row numbers.
Often when looping over rows of a worksheet we may use an integer to hold
the row numbers. However, there being over a million rows in Excel 2007 and
above, it is possible that the row number will exceed the maximum allowable
value of an Integer datatype hence resulting in an error.
Such an error where the value being assigned to a variable exceeds the
maximum allowable value for its datatype is called an ‘Overflow error’. The
solution is to use a sufficiently large variable. For holding row numbers, the
best variable in this case is a Long Integer.
6. Using invalid array indices
Sometimes when using an array variable, we write code that tries to access an
element whose index position is beyond the upper bound or below the lower
bound. For example, using the code arrTemp(0) when the lower bound or
arrTemp is 1. This results in an error called the “Index out of bound error”.
To avoid this situation, use the LBound and UBound functions described in
Chapter 8.
Appendix B
VBA primer, the Bare Essentials
In this chapter we will cover the most essential aspects of VBA programming
required for creating solutions in Excel.
B.1 Excel VBA Fundamentals
When we write a code for a macro, we write the instructions in Visual Basic
language. We can write a macro that does nothing to the workbook it is in, and
just shows a simple popup message.
But in order to automate Excel, we need to link (or instantiate) the object
variables in our macro with the real workbooks and spreadsheets. This is where
VBA comes in.
VBA has an entire set of objects (themselves called workbooks, worksheets and
cells) that can be linked to Excel Application, Workbooks, Worksheets and
everything contained therein.
Hence for creating a macro that operates on a workbook, we proceed as follows:
1. We declare one or more VBA objects using the ‘Dim’ statement.
Dim wbk As Workbook

Dim wks As Worksheet

Remember that this does not create a workbook or worksheet.

2. We tell the program to link this object to the actual workbook or worksheet
‘//** Open an existing workbook **//
Set wbk = Workbooks.Open(“C:\MyDocuments\Book1.xlsx”)

Or

‘//** Create a new blank workbook **//
Set wbk = Workbooks.Add

‘//** Link wks to a worksheet (sheet1) that is in wbk **//
Set wks = wbk.Worksheets(“Sheet1”)

3. Now we can use the methods and properties of visual basic objects to
automate the actual workbook and worksheets.
‘//** Save the workbook and save all changes **//
Wbk.Save(True)


B.2 Excel Object Model
As you would have rightly guessed from the code examples in the previous
section, a workbook contains a collection called ‘worksheets’ that is a set of all
worksheets in that workbook.
Similarly, a worksheet contains a ‘Cells’ collection that contains all the cells on a
worksheet.
In programming parlance, this arrangement is called an Object Hierarchy where
objects contain one or more other objects. This also means that if we have one
object in code, we can
1. Find its parent object: workbook of a given worksheet
2. Find its child objects: cells in a given workbook
We now list down the most important objects that are widely used for Excel
Automation.
The diagram here shows the most important objects arranged in a hierarchy.
Ovals represent collections of objects of a particular type, rectangles represent
individual objects.
Any object is contained in the object above it. Containment does not refer to the
actual design of the objects, but to the path to be taken to access a particular
object. The broken lines below Range object mean that althought Rows, columns
and Cells are mainly parts of the Worksheet object, individual Range objects can
also contain these objects. For example, in the Range B2:G20, the first column
will be B2:B20.
So, to get a reference to a particular range, we must first get a reference to the
Excel Application object, then to the collection of all open Workbooks. From
there we get a reference to a particular Workbook that contains the Range.
Then we get a reference to the Worksheets collection of the concerned
Workbook. From there we get to the Worksheet that contains the required Range.
We can then get a reference to the required Range.
Here are a few things to be borne in mind when writing macros. Please look out
for these when reading the code examples.
1. Since our macros are written inside Excel workbooks, the Application object
is available by default and we don’t need to get an explicit reference.

2. If the object (like Range or PivotTable) that we seek is inside the workbook in
which the macro is written, we can just use the ThisWorkbook object and
don’t need to use the Workbooks collection.

3. If a Worksheet is not explicitly specified, Excel assumes that the object we are
seeking is on the ActiveSheet (the Worksheet currently visible on the screen).
If some other workbook is open and visible on the screen, our code would
cause an error. Hence we will always specify a worksheet explicitly in our
code

B.2.1 Application (use spreadsheet functions, freeze the screen &


more)
This is the topmost object in the Excel object hierarchy. It gets created when
someone launches Excel from the Start menu or another program launches
Excel.
Usage:
To get access to properties and methods that can be used with all open
workbooks.
Properties
1. Workbooks: Returns a collection consisting of all workbooks currently open
in Excel.
2. WorksheetFunction: Allows us to use, inside a macro, some of the functions
that are normally used in a spreadsheet cell. The spreadsheet functions are
available as methods of the WorksheetFunction object returned by this
property.
Calculate Geometric Mean directly with numbers as arguments.

Dim dblValue as Double

‘//** dblValue = 5.0985 **//
dblValue = Application.WorksheetFunction _
.GeoMean(3, 4, 5.5, 6, 8.7)

Calculate Simple Average with an array.

Dim arrDbl() As Variant

‘//** Set values in the array **//
arrDbl = Array(3, 4, 5.5, 6, 8.7)

‘//** dblValue = 5.44 **//
dblValue = Application.WorksheetFunction _
.Average(3, 4, 5.5, 6, 8.7)

Sum values with a range object.
Range BD2:BD6 contains values: 3, 4, 5.5, 6, 8.7

Dim rng As Range

Set rng = _
ThisWorkbook.Worksheets("sheet3").Range("BD2:BD6")

‘//** dblValue = 27.2 **//
dblValue = Application.WorksheetFunction.Sum(rng)






3. WindowState: Used to control the size of the Excel window from VBA.

‘//** Window covers the full screen **//
Application.WindowState = xlMaximized

‘//** Window becomes an icon in the taskbar **//
Application.WindowState = xlMinimized

‘//** Excel Window becomes a small rectangle in the middle of the screen **//
Application.WindowState = xlNormal

The minimized state is the most useful. When a long macro is running and we
want to work on some other application (email or MS Word) we can minimize
the window. Then when the macro ends, we can bring back the size to
Normal.

4. ScreenUpdating: Used to stop/start screen updates when a macro is running.
If a macro opens several workbooks or makes changes to multiple worksheets,
the screen flickers.
Stopping screen updates, frees up the graphics memory and speeds up the
macro.
‘//** To turn off screen updates **//
Application.ScreenUpdating = False

‘//** To turn on screen updates **//
Application.ScreenUpdating = True

5. FileDialog: Used to display a dialog box through which user can select a file
or folder for further processing. This is covered in detail in section 14.2.

6. DisplayAlerts: Used to turn on/off the error and confirmation messages Excel
shows This keeps the code from stopping for a user confirmation.
For example, when a macro deletes a worksheet. Excel asks the user to click
‘Yes’ or ‘No’.
‘//** At the beginning of the macro turn Off the Alerts **//
Application.DisplayAlerts = False

‘//** At the end of the macro turn the Alerts back On **//
Application.DisplayAlerts = True

7. CutCopyMode: Used to turn off the cut/copy mode. When our macro copies
a range of cells, the values are saved in the Windows clipboard and we can see
“Ants” (white flickering dots) around the range.
Immediately after the data has been pasted at the destination, the copy mode
should be turned off to free up the clipboard memory and to avoid pasting the
data somewhere else by accident.

Application.CutCopyMode = False

At the time of writing the code, you would see the options ‘xlCut’ and
‘xlCopy’. Please don’t use these as we will be using the Copy methods of the
individual objects.

8. Calculation: Controls how Excel will calculate the formulas entered in
spreadsheet cells. The two important values are:

A) Manual mode
This mode stops Excel from automatically calculating the formulas that are
entered. This is especially useful when we are dynamically inserting formulas
in spreadsheet with our macro (see section 3.6 “Putting formulas in a
worksheet with VBA”).

In case the formulas are long and complicated, calculation may begin while
the formula is being copied into multiple cells, in such cases calculation can
hang up our macro and affect performance.

Or, we may want to insert formulas in multiple ranges, and calculate all of
them together.

Application.Calculation = xlCalculationManual

If we set the calculation mode to manual, we should ensure to change it back
to the default automatic mode towards the end of our macro.

B) Automatic mode
This is the default mode. It means Excel calculates the formula immediately
when it is entered into a cell.
VBA code for setting this mode is as follows:

Application.Calculation = xlCalculationAutomatic

9. ActiveWorkbook, ActiveSheet, ActiveCell:
These represent respectively the workbook, sheet and cell that is currently
selected and, is visible on the screen and is the top most window on the
screen.

By default, any keyboard input or pasting of copied content happens in the
active workbook/sheet/cell.

Since our aim is to write macros that work with multiple workbooks, which
may become active or inactive as the macro runs, we will not use these
objects.
However, if you frequently record macros, you will see them come up time
and again, so it’s better to be aware of them so you can change your code as
required.
Methods
1. Run: The most important use of this method is to run macros present in some
other workbook. We will see this method in detail in section 12.1, “Trigger
your macro when you are sleeping”.

B.2.2 Workbooks Collection (open a new or existing file)


Usage:
To get access to individual workbooks, create new ones, open a closed one.
Reference to a particular workbook of the collection is obtained by using the
name of that workbook.
‘//** Declare a workbook object **//
Dim wbk as Workbook

‘//** Reference to Input.xlsx **//
Set wbk = Workbooks(“Input.xlsx”)

Properties
1. Count: Returns an integer equal to number of workbooks open at any given
time.
Methods
1. Add: To create a new blank workbook. Returns a reference to the new
workbook that can be used to instantiate a VBA workbook object.
‘//** Declare a workbook object **//
Dim wbk as Workbook

‘//** Create a new blank workbook **//
Set wbk = Workbooks.Add

2. Open: To open an existing Excel workbook. Returns a reference to the
opened workbook that can be used to instantiate a VBA workbook object.
This method takes well over ten arguments. However not all of them are
useful.
We describe the ones that are most important.

a. FileName: The one essential argument. It is a string containing the path of
the workbook to be opened
‘//** Declare a workbook object **//
Dim wbk as Workbook

‘//** Open Input.xlsx workbook and get a reference to it **//
Set wbk = Workbooks.Open _
FileName:=“C:\MyReports\Input.xlsx”

b. UpdateLinks: Often colleagues send files that contain formulas or charts


linked to one or more workbooks that are still on their computers.
This mostly happens when functions like VLOOKUP have been used
across workbooks. In such case, a popup is shown asking the user if Excel
should update the links. Our macro hangs as long as we don’t provide an
answer.
The best way is to not update the links when opening the file. The code for
opening the workbook now looks like this:
Set wbk = Workbooks.Open _
FileName:= “C:\MyReports\Input.xlsx”, _
UpdateLinks:=False

c. Password: sometimes colleagues send over files that are password
protected. We need to provide the password in the code, else our macro
stops executing until the user keys in the password.
‘//** Open Input.xlsx workbook and get a reference to it **//
Set wbk = Workbooks.Open _
FileName:=“C:\Reports\Input.xlsx”, _
UpdateLinks:=False, Password:=<password>

3. OpenText: This is a short hand for the “Import External Data From Text
File” functionality provided by excel. We will not use this method.
Alternative methods for working with text files have been described in
Chapter 14.

B.2.3 Workbook Object (get the path, save, get all the sheets&
more)
Usage: To work on one individual open workbook.
For the code examples below, assume that we have an object ’wbk’ that
represents “C:\MyReports\Input.xlsx”. Our macro resides in the workbook
‘Report.xlsx’
Properties
1. Worksheets: Returns collection of all the worksheets inside a given
workbook.
2. Path: Returns a String containing the path to the folder where current
workbook is stored.
‘//** Declare a string variable **//
Dim strFilePath as String

‘//** strFilePath now holds “C:\MyReports\”. **//
strFilePath = wbk.Path

3. Name: Returns a String containing the name of the workbook.
‘//** Declare a string variable **//
Dim strFileName as String

‘//** strFileName now holds “Input.xlsx”. **//
strFileName = wbk.Name

4. FullName:Returns a String containing the path and name of the workbook.
‘//** Declare a string variable **//
Dim strFullName as String

‘//** strFullName now holds “C:\MyReports\Input.xlsx” **//
strFullName = wbk.FullName

5. Thisworkbook: Returns a reference to the workbook in which the macro is
running.

‘//** Returns the string “Report.xlsx”. **//
Thisworkbook.Name


Methods
1. Save: Used to save a particular workbook. The method does not take any
arguments

‘//Save the workbook referred to by the wbk object
wbk.Save

‘//** Save the current workbook that holds the macro **//
Thisworkbook.Save

2. Close: To close a particular workbook.
This method usually takes 3 arguments, normally only 1 of them is important
Arguments:
SaveChanges: This argument takes a Boolean value. Set this to ‘True’ if the
changes made in the workbook have to be saved. If the workbook is an input
file from which data is only read, this argument can be set to ‘False’.
wbk.Close SaveChanges:=False

To Save changes made to the current workbook (in which the macro is
written) the code would be.
Thisworkbook.Close SaveChanges:=True

3. SaveAs: The changes made to a workbook are now stored as a different


workbook. This method takes several arguments, the most important one is the
path of the new file.
wbk.SaveAs(“C:\MyReports\Input_1.xlsx”)

B.2.4 Worksheets Collection


Usage:
This is a collection, used to access worksheets inside a particular workbook.
For the code examples below, assume that in our code we have an object ’wbk’
that represents “C:\MyReports\Input.xlsx”. Our macro is written in a separate
workbook ‘Report.xlsx’
A particular worksheet of the collection is obtained by using its.
‘//** Declare a worksheet object **//
Dim wks as Worksheet

‘//** Reference to Sheet1 in Input.xlsx **//
Set wks = Workbooks(“Input.xlsx”).Worksheets(“Sheet1”)

Properties
1. Count: Returns a long integer value equal to number of worksheets in a
particular workbook.

Methods
1. Add: Used to create a new blank worksheets. Returns a reference to the new
worksheet that can be used to instantiate a VBA worksheet object.

‘//** Declare a worksheet object **//
Dim wks as Worksheet

‘//** Create a new blank worksheet in Report.xlsx **//
Set wks = Thisworkbook.Worksheets.Add


2. Delete: To delete an existing worksheet.
‘//** Turn off the Alerts **//
Application.DisplayAlerts = False

Workbooks(“Input.xlsx”).Worksheets(“TempSheet”).Delete

‘//** Turn the Alerts back On **//
Application.DisplayAlerts = True

Here we turn off the alerts of Excel before deleting the worksheet. Otherwise,
Excel would ask for user to confirm the deletion. Unless the user responds,
our macro will not work further.

B.2.5 Worksheet Object


Usage:
To work on one individual worksheet in an open workbook.
For the code examples below, assume that in our code we have an object ’wbk’
that represents “C:\MyReports\Input.xlsx”. Our macro runs in a separate
workbook ‘Report.xlsx’, which has a sheet named sheet1 represented by the
object ’wks’.
Properties:
1. Name: This is a string value representing the name of a particular worksheet.

‘//** Change name of worksheet from Sheet1 to FrontSheet **//
Wks.Name = “FrontSheet”

2. Cells: This is a collection of all the cells in a worksheet. Each cell can be
uniquely identified using its row and column number.
‘//** Cell C5, fifth row and third column **//
Wks.Cells(5,3)

It should be noted that each individual cell is a Range object.


3. Range: A range represents a group of one or more cells on a worksheet.
Being an important object in itself, we will look at it in detail in the next
section.

4. Columns: This is a collection of all the columns on a worksheet. A given
column can be obtained by using the column number or the column letters.
A given column from the columns collection is actually a group of cells and
hence it is a range object. So we can write code like:

Set rng = wks.Columns(“E”) OR Set rng = wks.Columns(5)

5. Rows: This is a collection of all the rows on a worksheet. A given row can be
obtained by using the row number.
A given row from the rows collection is actually a group of cells and hence it
is a range object.

‘//Sets the rng object to the entire 10th row
Set rng = wks.Rows(10)

Methods
1. Calculate: This method calculates all the formulas on a worksheet when the
calculation mode is set to ‘Manual’
wks.Calculate

2. PivotTables: This method returns a collection of all the pivot tables that are
present on a given worksheet.
A particular PivotTable can be obtained using the number or its name. We
cover PivotTables in detail in Chapter 5.
3. Copy: This method allows us to copy a worksheet within the workbook. The
main use is that it also allows us to save a worksheet to a new workbook.
The code below makes a copy of Sheet3 and places it immediately after
Sheet4 in the same workbook. Note that instead of ‘After’ we could use the
argument ‘Before’ to position the new copy before (to the left of) Sheet4
Thisworkbook.Worksheets(“Sheet3”).Copy _
After:= Thisworkbook.Worksheets(“Sheet4”)

If we don’t specify a ‘Before’ or ‘After’ argument, the sheet is copied into a


new workbook and, that workbook becomes the active workbook and we can
use it to instantiate a workbook object.
Dim wbk As Workbook
Thisworkbook.Worksheets(“Sheet3”).Copy
Set wbk = Activeworkbook

B.2.6 Range Object


A range represents a group of one or more cells on a worksheet. It is the most
fundamemtal object when working on Excel workbooks through macros.

Whereas the Cells property allows access to only one cell at a time, the Range
property allows access to either a single cell or a group of cells.

A particular range of a worksheet can be formed in two ways. In the code below
we show how to form a range object for the rectangular region with B1 as top
left cell and G25 as the bottom right cell.

A) By using the address of the range. We form a string with addresses of top
left and bottom right cells, and use that to create the range object.
For our example, the code would be:

Dim rng As Range

Set rng = wks.Range(“$B$1:$G$25”)

B) By using the top left and bottom right cells.
For our example, the code would be

Dim rng As Range

Set rng = wks.Range(wks.Cells(1,2),wks.Cells(25,7))

Properties
1. Address: This is a string value representing the address of a particular range
on a worksheet. The default syntax for this property is as follows:
‘//** Capture the address of a range into a string variable **//
strRangeAddr = rng.Address

However, this property takes 5 parameters (all optional) and these can be used
to return addresses in different formats. The complete code for this property
written along with parameters is as follows:
rng.Address(RowAbsolute, ColumnAbsolute, _
ReferenceStyle , External, RelativeTo)

The 4 most important ones are described below with code examples. If we
choose to use only 1 or 2 parameters, we need to leave commas for parameters
to the left, the ones to the right can be ignored. Code examples will make this
clear.
For the code examples listed below assume that our macro is in workbook
‘Report.xlsx’ and that there is another open workbook ‘ReferenceBook.xlsx’.
This latter book has a sheet ‘Lookup Sheet’ (notice the space in the name)
which has data in the range A5:B505.
Parameters:
RowAbsolute: This parameter takes a Boolean value. If the value is ‘True’, the
address string returned has its rows fixed. The default value is True.
‘//** Returns $A5:$B505 **//
rng.Address(False)

ColumnAbsolute: This parameter takes Boolean values. If the value is ‘True’,


the address string returned has its columns fixed. The default value is True.
‘//** Returns A$5:B$505 **//
rng.Address(, False)

ReferenceStyle: If this value is ‘True’, the address string returned has its rows
fixed. The default value is True.
‘//** Returns R5C:R505C2 **//
rng.Address(, , xlR1C1)
External: This parameter takes Boolean values. If the value is ‘True’, the
address string returned has the name of the worksheet and workbook on which
the range is actually present. The default value is False. Set the value to ‘True’
when creating a formula that refers to a sheet in another workbook.
‘//** Returns '[ReferenceBook.xlsx’]Lookup Sheet’!$A$5:$B$505 **//
rng.Address(,,,True)

2. Cells: This property returns individual cells in a range. Based on the number
of rows/columns the array can be 1 or 2 dimensional. Each individual cell is a
Range object

Each cell can be accessed individually by specifying its row and column with
the first row being row 1 and left most column being column 1. So in the
example above, the cell C5 is same as:

'//** Column C of spreadsheet is 2nd column of range, **//
‘//** row 5 of spreadsheet, is row 5 of range. **//
rng.Cells(5,2)

‘//** Cell C5, fifth row and third column **//
Wks.Cells(5,3)

Note: for the properties 3 to 9, refer to figure B.2.6. In the code examples,
the object ’rng’ refers to the range “A1:E9” shown in the figure. The object
’wks’ Is the worksheet containing this range. Most of the properties return a
reference to another Range. We can assign this to another Range object
’rngNew’
3. Rows: This is a collection of all the cells in a range. Each row can be uniquely
identified using its row number. The property returns a Range object that can
be set to another Range object
‘//** Reference to A6:E6 **//
Set rngNew = rng.Rows(6)


4. Columns: This is a collection of all the columns in a range. Each column can
be uniquely identified using its column number.
‘//** Reference to C1:C9 **//
Set rngNew = rng.Columns(3)

Fig B.2.6 Sample data


5. EntireRow: This object represents the full row that contains the entire
concerned range. In case the range has multiple rows, all those rows are
returned by this property.
‘//** Reference to 3:4 **//
Set rngNew = Wks.Range(“B3:B4”).EntireRow

6. EntireColumn: This object represents the full column that contains the entire
concerned range. In case the range has multiple columns, all those rows are
returned by this property.

‘//** Reference to B:C **//
Set rngNew = Wks.Range(“B3:C3”).EntireColumn

7. Worksheet: This property returns the parent worksheet (object)of a range. It
is most useful when the range reference is passed to another sub-routine and
the worksheet of the range is required there.

8. Offset: This property returns a range located at specified number of rows and
columns away from a given range. The range returned is of the same size
(rows and columns) as the range on which the method is applied. The syntax
is:

rng.Offset(rowOffset, columnOffset)

Parameters:
1. rowOffset: This is an optional argument. It is of Long Integer datatype and
represents the number of rows (positive, negative, or 0 (zero)) by which the
range is to be offset. Positive values are offset downward, and negative values
are offset upward. The default value is 0.

2. columnOffset: This an optional argument. It is of Long Integer datatype
and represents the number of columns (positive, negative, or 0 (zero)) by
which the range is to be offset. Positive values are offset towards right, and
negative values are offset towards. The default value is 0.

‘//** Reference to A6:B7 **//
Set rngNew = Wks.Range(“B3:C4”).Offset(3,-1)

9. Resize: This property resizes the specified range and returns a new object that
represents the new resized range.
rng.Resize(rowSize, columnSize)

Parameters:
1. rowSize: This is an optional argument. It is of Variant datatype and
represents the number of rows in the resized range. If omitted, the number
of rows remains same as the original range

2. columnSize: This is an optional argument. It is of Variant datatype and
represents the number of columns in the resized range. If omitted, the
number of colums remains same as the original range
The following code shows how the Offset and Resize properties can be
combined. Refer to figure B.2.6. It returns a reference to the range B2:C4

Set rngNew = Wks.Range(“B1”).Offset(1,0).Resize(3,2)

Methods
1. SpecialCells: Returns a Range object that represents all the cells that match
the specified type and value.
rng.SpecialCells(Type, Value)

The ‘Type’ argument is always required, while the ‘Value’ argument is


required if the ‘Type’ argument is xlCellTypeConstants or
xlCellTypeFormulas.
A complete list of the possible values for ‘Type’ and ‘Value’ arguments is
given below. We will see an important example of this method when using
Autofilter in section 4.2 “Working with filtered cells”
Valid values for ‘Type’ argument
Name Description

xlCellTypeAllFormatConditions Cells of any format.

xlCellTypeAllValidation Cells having validation criteria.

xlCellTypeBlanks Empty cells.

xlCellTypeComments Cells containing notes.

xlCellTypeConstants Cells containing constants.

xlCellTypeFormulas Cells containing formulas.

xlCellTypeLastCell The last cell in the used range.

xlCellTypeSameFormatConditions Cells having the same format.

Cells having the same validation


xlCellTypeSameValidation
criteria.

xlCellTypeVisible All visible cells.


Valid values for ‘Value’ argument
XlSpecialCellsValue
Value
constants

xlErrors Cells containing Errors


Cells with True/False
xlLogical
Values

Cells with Numeric


xlNumbers
values

xlTextValues Cells with Text values

2. Copy: Copies a given range to another range (in case the ‘Destination’
argument is specified) or to the Clipboard.
If copied to clipboard, the ‘CutCopyMode’ has to be turned off after the range
is pasted to another location.

rng.Copy(DestinationRange)

The destination range should be the top left corner cell of the range where we
want to paste the copied range.
The example below copies a range A1:D7 from Sheet2 to Range E5:H11 of
Sheet1.Notice that for the destination argument we have specified only the
top-left corner of the destination range.
Set rng = ThisWorkbook.Worksheets(“Sheet2”).Range(“A1:D7))
rng.Copy(ThisWorkbook.Worksheets(“Sheet1”).Cells(5,5))

We show an example without the ‘Destination’ arugment when we describe


the ‘Paste’ method below.
3. ClearContents: This method will remove the data and formula from a range
but retain the formatting. This is different from deleting the range which
removes data as well as formatting. Also note that deleting a range will caue a
#REF error in the formulas that depend on that range. Clearing the contents
will provide a 0 value to the formulas

4. Paste: This method will Paste the contents of the Clipboard into the
concerned range. Syntax for this method is as follows.
rngTarget.Paste(bLink)

The Boolean argument bLink is optional and is used to specify if we want to


paste a link to the contents or the contents themselves. Default value is ‘False’
which means that the contents are pasted in the range. If set to True, excel will
insert formulas linking each cell of the target range to corresponding cell of
the source range.
We continue with the example in the ‘Copy’ method described above. Notice
how we don’t specify the ‘Destination’ argument when copying and also how
we switch off the CutCopyMode.
Set rng = ThisWorkbook.Worksheets(“Sheet2”).Range(“A1:D7))
Set rngTarget = ThisWorkbook.Worksheets(“Sheet1”).Cells(5,5)
rng.Copy
rngTarget.Paste
Application.CutCopyMode = False

If the link argument is set to ‘True’ Cell E5 on sheet1 will have a formula
“=Sheet2!A1”. Similarly, cell G10 on Sheet1 will have the formula
“=Sheet2!C6”.
5. PasteSpecial: This method represents the PasteSpecial option available on the
spreadsheet. Its most important use is to convert formulas to values in a range.
Syntax for the method is as follows
rDst.PasteSpecial(PasteType, Operation, SkipBlanks, Transpose)

In the above line of code, ’rDst’ is the destination range (the one where we
want to paste copied data)
In case the copied data contains more than one cell, ’rDst’ will be the top left
cell of the destination range.
Arguments:
1. PasteType: This argument dictates what part of the copied data is pasted.
Value of this argument is one of the xlPasteType named constants. There
are in all 12 constants.

A cell on a spread sheet can have Formula, a Value (calculated by the formula
and displayed in the cell), some formatting in terms of cell color, some border,
Comments, some cell validation and so on.

When using PasteSpecial method, we can control what gets copied to the
destination range. The most useful values for this argument are listed in the
table below. The default value is ’xlPasteAll ’

Name Description
xlPasteAll Everything will be pasted.
Format from the source range is
xlPasteFormats
pasted.
xlPasteFormulas Formulas are pasted.
Formulas and Number formats are
xlPasteFormulasAndNumberFormats
pasted.
xlPasteValues Values are pasted.
Values and Number formats are
xlPasteValuesAndNumberFormats
pasted.

2. Operation: When a numeric value is copied, we can use it to perform
mathematical operations by pasting it on another range having numeric values.

The Operation parameter takes its value from one of the predefined constants
and specified the type of operation to be done.

Name Description
Copied data will be added to the values in the
xlPasteSpecialOperationAdd
destination range.
Copied data will divide the values in the destination
xlPasteSpecialOperationDivide
range.
Copied data will be multiplied with the values in the
xlPasteSpecialOperationMultiply
destination range.
xlPasteSpecialOperationNone No calculation will be done in the paste operation.
Copied data will be subtracted from the values in
xlPasteSpecialOperationSubtract
the destination range.

We consider 2 examples, one for division and the other for multiplication, but
the concepts are applicable to all the operations allowable in PasteSpecial
method.

Example 1
Suppose we have closing stock prices in cells C2 to C501 of the worksheet
“StockPrice”. The stock has undergone a 2:1 split so all the prices have to be
divided by two. We proceed as follows (we should ensure that the cell AA2 is
blank)
With Worksheets(“StockPrice”)
.Range(“AA2”).Value = 2
.Range(“AA2”).Copy
.Range(“C2:C501”).PasteSpecial _
xlPasteValues, xlPasteSpecialOperationDivide
.Range(“AA2”).ClearContents
End With
Application.CutCopyMode = False


Example 2
Suppose for a group of 50 workers we maintain data in a spreadsheet in rows 2
to 51.
Column A contains Names of workers, Column B has the number of hours
worked in a week and Column C has the hourly pay.
We assume that the current sheet named “TempSheet” is a temporary sheet so we
don’t mind changing the values in some of the cells permanently.
We want to calculate the total amount we will be paying this week. We do this
by multiplying the values in column B with values in Column C.
We proceed as follows.
Dim dblAmt as Double
With worksheets(“TempSheet”)
.Range(“B2:B51”).Copy
.Range(“C2:C51”).PasteSpecial _
xlPasteValues, xlPasteSpecialOperationDivide
Application.CutCopyMode = False
dblAmt = _
Application.worksheetFunction.Sum(.Range(“C2:C51”))
End With


We now continue with the description of the remaining two arguments
3. SkipBlanks: This is a Boolean argument and controls if blank cells that
have been copied should be pasted in the destination. The default value is
‘False’ (don’t ignore the blanks). Setting this property to ‘True’ will ignore
blank copied cells. We will always use the default value of ‘False’.

4. Transpose: This Boolean argument specifies if the rows of the pasted
range should be turned to columns and vice-versa. Most of the time we will
use the default value of ‘False’ meaning - don’t transpose at the time of
pasting.

6. Find: This method finds specific information in a range, and returns another
range object (a single cell) that represents the first cell where that information
is found. The syntax is as follows:
rngSource.Find(What, After, LookIn, LookAt, SearchOrder, _
SearchDirection, MatchCase, MatchByte, SearchFormat)

The range in which the search will be conducted is denoted by ’rngSource’ we
will call it the “Source Range”.

Arguments:
1. What: This argument is the value that we want to find, it can be of any
primitive datatype.
2. LookIn: This argument specifies the part of contents of the source range
where the search is conducted. It can take one of the following values
a)xlValues: The values in the cells are checked even if the values
are results of formulas in the cells.
b)xlFormulas: The formulas in the cells are checked. For example, we
can search for the word “look” to see if any cells in the
range have a “VLOOKUP” formula.
c)xlComments:The comments in the cells are checked to see if they
contain the value that is being searched.
3. LookAt: This argument is used to specify if a partial match is acceptable or
we want an exact match. We will explain this with examples along with the
values that this argument can take. The acceptable values are as follows:
a) xlPart: If the argument ’What’ has the value “King” then even cells
containing the words “Kingdom” or “Shrinking” are returned
as search results.
b) xlWhole:Only the cells that contain the work “King” are returned.
Note that the word “King” may be in upper or lower case.
4. MatchCase: This is a Boolean argument. If set to TRUE, the search
becomes case sensitive. Meaning if ’What’ has the value “King”, then the
cells with value “KING” or even “kINg” are not included in the results. If
omitted, the default FALSE is used.
The following arguments are applicable only in special cases and are not
useful in routine work. These are optional arguments and can be safely
omitted. We will describe them briefly but not use them.

MatchByte: Applicable only when using Double Byte


character systems mainly used for non-english
characters represented by If we want to enable
Double Byte character search, we should set this
argument to TRUE, else use the default value of
FALSE.
SearchOrder: Applicable only when the Source Range has
multiple rows and multiple columns. This
argument specifies if the search should take place
first by rows in a columns and then do the same
across the other columns or the other way around.
Possible value: ’xlByRows’ or ’xlByColumns’
SearchFormat: Applies only when we are searching for cells with
a particular format. The value is the search format
to be found.
SearchDirection: Specifies if the search will proceed to find the
next matching value (Default) or go to a
previously found value.
Possible value: ’xlNext’ or ’xlPrevious’

Important point about Find method:
a) The Find method returns a Range object. So it should be used to set a
Range object variable. Here is the syntax

Dim rngFound as Range

Set rngFound = rngSource.Find(. . . .)

b) If no matching value is found, then the Find method returns Nothing. We
can check if a value was found by using the following code
If rngFound Is Nothing Then
‘//Code to take action when no matching value is found
End If

7. FindNext: Continues a search (on the same source range) that was begun with
the Find method. Finds the next cell that matches those same conditions and
returns a Range object that represents that cell. Doesn’t affect the selection or
the active cell. The syntax is as follows:

rngSource.FindNext(rngAfter)

Arguments:
rngAfter: This argument is a range (a single cell) beyond which we want to
find the next value. For example, rngSource is A1:A500 and rngAfter is A5,
then the new search will be in the range A6:A500.
Usually ’rngAfter’ is the range returned by a previous execution of the Find
method. To continuously find one value after another we can use a code like
so:

Set rngFound = rngSource.FindNext(rngFound)

We should use such code with care because it can result either in an error
(when nothing is found in the previous search) or in an infinite loop because
when the search reaches the end of the specified source range, it wraps around
to the beginning of the range.
In sections 3.2 we will see detailed examples of proper use of the Find and
FindNext methods.
8. Replace: This method first finds one set of specified characters in a range and
then replaces them with with another set of specified characters. The syntax is:
rngSource.Replace(What, Replacement, LookAt, SearchOrder, _
MatchCase, MatchByte, SearchFormat, ReplaceFormat)

The objects and arguments ’rngSource’, ’What’, ’LookAt’, ’SearchOrder’,
’MatchCase’, ’MatchByte’ and ’SearchFormat’ have the same meaning as
described for the Find method a bit earlier. The other arguments are as
follows:
1. Replacement: This argument holds the value that will be replaced in place
of What. It should be of the same datatype as What.
2. ReplaceFormat: Applicable only when we use SearchFormat. It specifies
the changed format to be applied to the format that was earlier searched for.
In sections 3.3 we will see detailed example of Replace method.

B.2.7 PivotTable Object


A PivotTable object represents an individual PivotTable created on a worksheet.
Every worksheet has a collection of PivotTables. This collection is obtained
using the ‘PivotTables’ method of the worksheet object. Once this collection is
obtained, a particular pivot table can be obtained using either its number or,
more appropriately, its name.
Chapter 5 is devoted to working with PivotTables from VBA and we will
describe the important properties of the PivotTable object in that chapter.
B.3 Excel Events
The Worksheet and Workbook objects in Excel provide a wide variety of events
that we can often put to good use. Please see section C.4 for a brief description
of the general concept of ‘Events’.
To reiterate, Events are sub procedures that get executed when some action is
taken on the Excel objects by the user, operating system or a macro. In this
section we will see basic techniques for using the events provided by Excel.

B.3.1 Accessing the events


To access the events for Workbook or Wrksheets, first open the Visual Basic
Editor. In the Project Explorer, double click on one of the sheets or on
‘ThisWorkbook’. This will open the code module for that object. Figure B.3.1a
shows the code module for ‘Sheet3”
In the left drop down (Object dropdown) select ‘Worksheet’. If we now expand
the dropdown on the right (Procedure dropdown) we will see a list of various
events that are available for the object.

Fig B.3.1a The code module for a worksheet. Events listed in the Procedure Dropodown

The names of the events tell us when the events will be triggered. When we
select any one event, Excel will insert an empty sub procedure in which we can
put code that will get executed when the event is triggered.
The entire list of events is not of much use. We will list down some of the events
that we can use frequently along with a brief explanation of their arguments.
Reader is requested to explore the other events.
Specific code examples won’t be provided here. Examples can be found in
sections 17.5, and section 12.1.

· Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)

Occurs before the workbook is saved either by a user or by a macro.
Inside the code for this sub, we should set the argument ’SaveAsUI’ to True if
we want to show “Save As” dialog to the user.
The argument ’Cancel’ has the value False when the event is triggered. If inside
the sub we set this argument to True, the workbook will not get saved.
· Workbook_Open()

Occurs when the workbook (holding the code for the event) is opened either
manually by the user or by a macro.

· Worksheet_SelectionChange(ByVal Target As Range)

Occurs when on a worksheet the user clicks on (selects) any one cell or uses
arrow keys to move to another cell.
The argument ’Target’ is the newly selected range.
This event is a bit annoying because it gets triggered even when user moves to
another cell without any specific purpose. If we want our code to run only when
a specific cell is selected, we should compare the address of ’Target’ range with
our desired cell.

· Worksheet_Change(ByVal Target As Range)

Occurs when contents of the worksheet are changed, either manually by the user
or through a macro.
This event is not triggered if only the formatting of the content is change. This
event is better than ’Worksheet_SelectionChange’ because it is not triggered
every time the user moves to a new cell.
The argument ’Target’ is the range that has been changed.
The event is not triggered when cells change due to a calculation. There is a
separate event called ’Worksheet_Calculate’ that we can use to respond to
calculations.
Appendix C
Visual Basic Fundamentals
Before learning VBA (Visual Basic for Applications), we need to understand
some fundamentals of VB (Visual Basic) which is the base for VBA. This
appendix will help the reader learn the most basic concepts required for
programming with Excel VBA.
The emphasis here is not to delve deep into programming techniques. The aim is
to learn a good variety of general concepts that can be safely combined and
applied to create a wide array of solutions. Let’s get started.
C.1 Writing code - The Visual Basic Editor
Code for macros is written in an interface called the Visual Basic Editor (VBE).
This is Microsoft’s IDE (Integrated Development Environment) for writing
macros.
You can access the VBE in two ways
1) One option is to press ‘Alt + F11’
2) Another option is as follows.
In the main ribbon, ensure the ‘Developer’ tab is visible.

If the developer tab is not visible, go to: File menu -> Click options in the left
hand pane. In the ‘Excel Options’ dialog box that comes up (see figure C.1a),
click on ‘Customize Ribbon’ on the left pane (marked by number 1) and check
the ‘Developer’ Option in the list on the right (marked by number 2). Finally,
click on OK.

Fig C.1a Visual Basic Editor



On the Developer tab, click on ‘Visual Basic’ (shown in figure C.1b)

Fig C.1b Visual Basic Editor



The Visual Basic Editor (VBE) opens in a separate window. The editor consists
of various parts as shown in figure C.1c. Each of these is actually a window in
itself. However, in the default view, these are neatly positioned for comfortable
operation.

Fig C.1c Visual Basic Editor


1. The Project Explorer
This is located on the top half of the left side in the default layout. If Project
Explorer is not visible, go to the ‘View’ menu and select ‘Project Explorer’ or
press ‘Ctrl + R’ on the keyboard.

The Project Explorer lists all the code modules, class modules and UserForms
that are used in the current workbook. Modules are areas that hold our code.

By default, Excel adds modules for the current workbook and worksheets that
are present in the current workbook. These are mainly used to hold the code for
Events (code that executes when something happens) related to these objects.

If we double-click on any of these objects (except for a UserForm), we get the
code module of that object on the right. For a UserForm, the form itself opens up
and we have a different way to access its code which is described in Chapter 20.

If we want to add our own modules we can do as follows.
A) Go to the ‘Insert’ menu and select any one of Class, Module or UserForm.
The new module will appear in the Project Explorer

B) Right click anywhere in the Project Explorer. In the pop-up menu select
‘Insert’ and in the sub-menu that comes up, select any one of Class, Module or
UserForm

We will deal only with Standard Module and UserForm module. Class Modules
are used by advanced programmers to create their own application objects and
these are out of the scope of this book. Interested readers can refer to any book
dedicated to Visual Basic programming.

2. The Properties window
This is located in the bottom half of the left side in the default layout. If this
window is not visible, go to the ‘View’ menu and select ‘Properties Window’ or
press ‘F4’ on the keyboard.
This window allows us to set the properties of various modules and controls
(when designing forms). The property common to all types of modules is the
module’s Name. However, when we work with UserForms we can set various
other properties of forms and the controls (buttons, dropdowns and so on) placed
on the forms. We will see extensive use of this window in section 3 of this book
in Chapter 20.
Although properties can be set for the workbook and worksheet modules, but it
is recommended to preserve the default values.

3. The Code window
This is located in the upper right part in the default layout and occupies the
largest part of the screen. This window displays the code for a module that is
selected in the Project Explorer.
If the code window is not visible, we can select the relevant module and press
‘F7’ on the keyboard. For modules other than UserForms we can simply double
click on the module to display the related code.

The code window has two dropdowns near its top (see figure C.1c). Let’s see a
brief description of each of them.
a) Object Dropdown
This dropdown is located at the top left and always contains the entry
“(General)” irrespective of the type of module.
For modules related to specific objects like Workbook, Worksheet, UserForm
and so on, there is an additional entry – the name of the object type
(Workbook, Worksheet…)
Making a selection in this dropdown affects the list of items available for
selection in the Procedure Dropdown.

b) Procedure Dropdown
This dropdown is located at the top right. The items available for selection in
this dropdown depends on the selection made in the Object dropdown.
For modules related to specific objects, if we select the name of the object,
this dropdown lists the events available for that object. The following
figureshows the list for Workbook object.

For a standard Module, this dropdown lists the name of all the sub and
function procedures created in that module.
Selecting any event or procedure name, positions the cursor in the area where
that procedure is declared
If the entry “(General)” in the Object dropdown, we get an entry
“(Declaration)”. Selecting this entry will position the cursor at the beginning
of the module where module level variables are declared.

4. The Immediate window
This window is used to see program output using the ‘Debug.Print’ command.
This window is very useful when we are in the process of developing our code
and we want to see if the code written upto a certain point is working properly
before we execute the code on files or workbooks.
For example, while we are still developing our macro, if we are compiling a
string holding the path of a file that we will later open, it is better to print the
path in the Immediate window to check it, instead of executing it directly and
getting an error in case the path is incorrect.

Syntax:
At several places in this book we will come across the term ‘Syntax’.
Syntax is the specific ordered combination of words that forms one single
program instruction.
In any line of code if we change the position of a single character from the
prescribed syntax the line is treated as invalid and results in an error.
Following is the correct syntax to get a reference to the second worksheet in a
workbook (we will see the details in appendix B)

Set wks = wbk.Worksheets(2)

The following is the incorrect version of the same line of code. Notice that one
missing word causes an error.
Wks = wbk.Worksheets(2)


One important thing we need to ensure when writing code in Visual Basic Editor
(VBE) is that the ‘Intellisense’ feature is turned on.
Intellisense helps us to use the correct syntax when writing our code. It provides
the complete list of all properties and methods for any object along with the
correct spelling for the property and method names. Further it gives us the
correct order and name for arguments of methods, sub procedures and functions
(we will discuss all of these concepts shortly).

The following figure shows how Intellisense gives us the correct syntax (in this
case the entire list of arguments) for the Open method of a Workbooks object.

This helps avoid coding errors and also saves us the effort of memorizing the
exact names and positions of arguments.

By default, the Intellisense feature is turned on when we open the VBE.
However, it is better to do a quick check before writing any code. In VBE, go to
the ‘Tools’ menu and click on ‘Options…’. The ‘Options’ dialog box comes up
(refer to figure C.1d).

Fig C.1d Visual Basic Editor options

Ensure that all the options in


the ‘Code Settings’ group are
selected (checked) as shown
in the figure.
C.2 Line continuation and Comments
Line continuation
Each program instruction in a macro can be written in one single continuous
line. However, sometimes a single instruction can get too and the full line may
not fit in the width of the window on our screen. This affects readability.
In such cases we may want to break up a single line of code over to the next line.
This is done using the Line Continuation character ( _ ) , a space followed by an
underscore.
For example, the following two lines of code are equivalent

I) rng.Copy _
(ThisWorkbook.Worksheets("Invtemp").Range("$R$1"))

II) rng.Copy(ThisWorkbook.Worksheets("Invtemp").Range("$R$1"))

Here, rng is a Range object. We will cover Objects in upcoming sections in the
current appendix. The Range object is a group of cells on an Excel worksheet
and is described in detail in Appendix B. Details of the object are not required to
understand the line continuation concept.

Comments
Comments are lines that are not executed when we run our macros. Comments
allow us to leave remarks about what a particular piece of code does. Hence they
allow us to provide documentation about the code mixed with the code itself.
This helps in two ways
1. Few days or weeks down the line if we want to make any changes to the code,
we might forget a lot of functionality but comments would help us recall what
we have created.
2. If our code is read by other programmers, comments would help them
understand our code.
Here is an example.

‘//** Copy the contents of rng to the worksheet named InvTemp **//
‘//** with upper left corner located at cell R1 **//
rng.Copy(ThisWorkbook.Worksheets("InvTemp").Range("$R$1"))

A comment starts with a ('). If a (') occurs in a line after some code (on the same
line), all characters after the apostrophe are ignored.

As in our example, sometimes we may want to comment multiple lines. Instead
of putting an ( ' ) in each individual line, we can do as follows
1. Type the lines as usual
2. Select all the lines to be commented, on the toolbar, click on the “Comment
Block” icon as shown in figure C.2.1a
Fig C.2.1a The Comment Block and Uncomment block
icons on the Edit toolbar.


If the Comment or Uncomment Block icons are not visible, do the following:
1. In the Visual Basic Editor, click on the ‘View’ menu.
2. Hover the mouse pointer over the “Toolbars” option to open a small Sub-
menu
3. Click on the “Edit” option to show the editing toolbar. This toolbar can be
dragged and positioned just like a normal windows tool bar. It should contain
the two required icons.
Comments can be used to deactivate certain lines of code. For example, lines
that we need only during testing purpose or lines that we have kept as options to
the primary code. For example, the second line of code shown below will not be
executed.

rng.Copy(ThisWorkbook.Worksheets("InvTemp").Range("$R$1"))

‘rng.Copy(ThisWorkbook.Worksheets("Invoice").Range("$A$1"))
C.3 Primitive Datatypes, Variables, Expressions and
Declaration.
In a computer program, we need to tell the computer about the real world items
that the program would be dealing with. As an example, suppose we have a
program that calculates Sales Revenue, we have the formula
Revenue = Units Sold x Price per Unit
The computer does not understand things like ‘Revenue’ or ‘Price’. However, it
does understand things like whole numbers, decimal numbers, single letters,
string of letters and so on.
In our example, assume for now, that Units sold would be a whole number or
‘Integer’ like 5000 books or 1000 blenders.
Revenue and Price would be dollar figures, numbers with fractions. These are
called floating point numbers.
If each product sold has a product identification number, it will most likely be
some form of alpha-numeric identification. For example, “CPU00010A590”.
This is a ‘String’.
Integer, Float and String are called primitive or native datatypes, things that the
computer understands.
Following are some points to bear in mind when writing macros:
Ø Every representation of a real world item that is processed by our
program/macro is called a ‘variable’.
Ø Variables are given names so that the program and the programmer can
identify them.
Ø There are certain words called ‘reserved words’ or ‘keywords’ that are used
exclusively for writing program instructions. These should not be used as
variable names.
Ø In reality, a variable is a name given to some space reserved in the computer’s
memory. Our macro can then store values in that space or change values that
are already stored.
The act of telling the program which item of the real world corresponds to which
datatype is called ‘Declaring’ the variable. This is done using the ‘Dim’
keyword.
Declaring the variable tells the program to reserve some space in the memory
(RAM) where different values for that variable can be stored.
Continuing with our example, the declaration would go like this
Dim unitsSold As Integer
Here we are saying,
“Hey macro, whenever I say unitSold, you should think of it as an integer.
In the computer’s memory, please create space for one more integer. I will tell
you what value to put there at different times in the program.”
Important points about primitive datatypes:
Ø Conceptually, primitive datatypes cannot have any subcomponents. They are
the basic building blocks.
Ø Each datatype can hold only certain range of values. Like maximum value for
a number or maximum number of characters in text. Exceeding this limit
would cause errors. Fortunately, datatypes have a large enough limit to cover
all practical purposes.
Expressions:
In the discussion that follows, we will use the term ‘expression’ at many places.
An expression is a line of code that consists of variables and operations on
variables. For example, at the beginning of this section we saw the following
expression
Units_Sold x Price_per_Unit

Here ’Units_Sold’ and ’Price_per_Unit’ are variables and the operation is that of
multiplying the two variables.
Operations are performed using ‘Operators’. For example, multiplication is
performed using ( * ) and not (x) as shown here. We will discuss operators in
detail in an upcoming section.
Expressions produce a result or value. The result is of a particular datatype. This
result can be used in the following ways
a. Place the result in another variable. For example, when we write
Revenue = Units_Sold x Price_per_Unit

We are placing the result of the expression (right hand side) in the variable
‘Revenue’ on the left hand side.
The act of putting a value in a variable is called ‘Assignment’. It is an
operation and is performed using the assignment operator ( = ).
The variable on the left hand side should be of the appropriate datatype. For
example, in our case, ’Units_Sold’ would most likely be an integer and ’Price_
per_Unit’ would be a decimal number (Double datatype). The result of
multiplication will be a Double and so the variable ‘Revenue’ should be
declared as a Double.
An error occurs if the datatype is not compatible or if the value produced by
the expression is outside the range of the variable on the left hand side.

b. Place the result in a cell of a worksheet.
Worksheets(“Sheet3”).Range(“B2”).Value = _
Units_Sold * Price_per_Unit
An expression need not be limited to only two variables. We can have several
variables and operators as well as constants. The following line of code
calculates a hypothetical adjusted amount
adj_amount = (((Units_Sold * Price_per_Unit) - 13)/100) + 50

Note that whenever we have a long expression with several variables and
operators we must use parentheses or brackets to make the results clear. For
instance, in the expression above it is clear that
1. ’Units_Sold’ should be multiplied with ’Price_per_Unit’
2. From result of step 1 a fixed value of 13 should be deducted using the
substraction operator ( - )
3. Result of step 2 should be divided by 100 using the division operator ( / )
4. Result of step 3 should be added to 50 using the addition operator ( + )
We could have written something like:
adj_amount = Units_Sold * Price_per_Unit - 13/100 + 50

But then the result would not be what we intended.


There are mathematical rules (called operator precedence) built into
programming languages that decide the order of operations in absence of
parentheses. However, over-dependence on these rules has the following
disadvantages:
a) We have to memorize the precedence rules.
b) Makes the code difficult to understand if we want to change it few months
down the line.
c) If we are not careful, such practice may produce results different than what we
intend to produce.
To avoid all these hassles it is advised to make use of as many parentheses as
required when writing complex expressions.
Now let’s continue with exploring the primitive datatypes. There are a wide
variety of datatypes but, at the basic level, for working with Excel, we need the
following data types.
1. Boolean: Used to hold one of the two values - True or False. The syntax for
declaration of a Boolean variable is as follows:

Dim bCheck As Boolean

Every Boolean variable occupies 2 bytes in the computer’s memory.


We can assign a value to a Boolean variable as shown in the following line of
code:
bCheck = TRUE

A Boolean variable can also take its value from an expression that results in a
True or False value. In the following code ’bCheck’ will get a value TRUE if
’number1’ is greater than ’number2’ else it will get a value of FALSE. The
variables ’number1’ and ’number2’ will be numeric datatypes like integer or
double
bCheck = number1 > number2

2. Date: Used to hold date & time values. Range of values for dates is 1 Jan 100
to 31 Dec 9999 and range for time is 00:00:00 to 23:59:59. The syntax for
declaration of a date variable is as follows:
Dim dtStart As Date

Every date variable occupies 8 bytes in the computer’s memory.


The guidelines on using this datatype and for assigning values to it are
discussed in Chapter 2, section 2.3 and Chapter 8, section 8.3.
3. Double: Used to hold decimal numeric values. Range of values are as
follows:

For negative values -1.79 x 10^308 to -4.94 x 10^-324
For positive values 4.94 x 10^-324 to 1.79 x 10^308
‘x 10^308’ means we multiply with 10 followed by 308 zeroes
‘x 10^-324’ means we divide by 10 followed by 324 zeroes

The syntax for declaration of a double variable is as follows:

Dim dblAmount As Double
Every Double variable occupies 8 bytes in the computer’s memory.
4. Integer: Used to hold integral or whole numbers. The range of values is
-32,768 to 32,767. The syntax for declaration is:
Dim nPices As Integer
Every Integer variable occupies 2 bytes in the computer’s memory.
5. Long: Also called Long Integer, this type of variable is used to hold very
large integral values. The range of values is -2.1 Billion to +2.1 Billion. The
syntax for declaration is:

Dim lRows As Long
Every Long variable occupies 8 bytes in the computer’s memory. The best use
of this datatype is for holding row numbers of a worksheet since new Excel
versions have over a million rows.
6. String: Used to hold text having alpha-numeric and other printable characters.
These can be fixed or variable in length. However, we will deal only with
variable length strings since they are most useful when reading data from files
and worksheets.
Variable length strings can contain upto 2 billion characters. The number of
bytes occupied in memory by a string is equal to the number of characters in
the string.
The syntax for declaration is:

Dim strFileName As String

7. Variant: A variant datatype can hold values of any of the other datatypes. We
should be extremely careful when using variants because it may happen that a
piece of code would perform a mathematical operation on a variant, but
elsewhere in our code we might have assigned an alpha-numeric string to the
variant.

The only time we will use a Variant datatype is when reading an array (see
section C.7 and section 3.6 in Chapter 3) of values directly from cells on a
worksheet. The syntax for declaration is:

Dim arrCellValues As Variant

8. Application specific objects: When we try to automate other applications
like Excel, Word and Powerpoint, we have to represent the items in those
applications within our macro.
We then have to create variables representing those specific items. Such items
are called objects of the respective application. We will discuss the concept of
objects in detail in the next section.
Such objects are used for connecting what we see on screen to the code of our
macro. Following are some examples for declaring objects:
Dim wbk As Workbook
Dim wdDoc As Word.Document
Dim ppFile As Powerpoint.Presentation
C.4 Programming for the real world – Objects,
Methods, Properties
Any item or entity of the real world that we represent in our program is too
complex to be represented by a single variable of the basic datatypes like a string
or integer. Real world items require a combination of variables to represent them
and their behavior. Examples are
1. A Car. A car would have a model name (String), may or may not be a 4-Wheel
Drive (Boolean), would have a wheel base (Double),
2. An Excel workbook. A workbook would have a name(String), a path where it
is stored(String), the number of worksheets it contains (Long Integer).
Such complex items when used as variables inside a macro or program are called
‘Objects’.
Along with items of primitive datatypes, objects can also contain other objects.
Those objects can contain still other objects. For example,
1. Every car can have 4 ‘Wheel’ objects. Each wheel can have a Diameter
(Double), a tread pattern (represented by String). Width (Double)

2. Every Excel workbook can contain one or more Worksheets. Each worksheet
would have a Name. It would also have a collection of cells and also groups of
cells called Ranges. The individual cells and ranges are objects contained
within a Worksheet object that is contained within a Workbook object.
Further, within an object, are available special procedures (blocks of code or
program instructions) called ‘Methods’ that allow us to modify the data held
within the object. The data contained within an object, in the form of primitive
datatypes or other objects, are called ‘Properties’ of the object. Let’s explore
these concepts in a more detail
v Properties
The different pieces of data as well as the other objects contained within a main
object are called the “Properties” of the main object. Properties can be accessed
using their respective names and the dot ( . ) operator. For example, if ’wbk’ is a
workbook variable its name can be accessed and captured in a string variable
like so:
Dim strwbname As string

strwbname = wbk.Name

The dot operator can be consecutively applied in the same line of code to
retrieve properties of objects within objects. For example, the “Worksheets”
property of a workbook gives us the worksheets object (a collection object which
we will describe in the next section) that is a set of all the worksheets contained
inside a workbook. This object has a “Count” property that gives us the total
number of worksheets contained. The “Count” property is a Long Integer and
can be accessed as follows:
wbk.Worksheets.Count
v Methods
Objects contain built in ‘function’ and ‘sub’ procedures, that allow us to operate
on the properties of the object. We will describe function and sub procedures in
detail in section C.6 “Code Groupings”.
For now, consider that the “Worksheets” object has a method called “Add” that
creates a new “Worksheet” in the related Workbook. To add a new worksheet,
the code will be:
wbk.Worksheets.Add
v Events
Events are special types of sub-procedures within an object that get executed
when the user, or the operating system or the application itself takes some action
involving that object. When the sub starts executing it is said that the event is
triggered or that the event has occurred. Events are useful for capturing user
interactions. Examples of events are:
a) The “Click” event that gets triggered when a user clicks a button on a form.
b) The “Open” event that is triggered when a workbook is opened either
manually by the user or via a macro.
Creating events for objects requires advanced programming knowledge. We will
just use the events that are built into the objects in Excel and other applications.

C.4.1 Declaring and Setting Objects


When we declare a variable of primitive data type we immediately get a memory
location to work with. This is not the case with objects.
When we declare an object variable the actual object is not created in memory.
Instead a location is created that would hold a reference to some actual object.
This reference is actually like a long integer but we don't need to worry about its
actual datatype.
Once we declare an object variable (which is actually a reference to an actual
object) we need to link the object reference to the actual object.
The act of linking an actual object to a reference is called “Setting” a reference.
It is done using the “Set” statement. Setting a reference is sometimes also called
‘instantiating’ the object. We will use the terms ‘setting’, ‘instantiating’ and
‘initializing’ interchangeably all the while meaning that we are connecting an
object variable to an actual object.
Once we have a reference, there are two possibilities, let’s look at them one by
one.
1. The actual object may already be in the computers memory
For example, when a workbook is opened manually the workbook and all the
worksheets in it are created in the computers memory. If we want a macro to
work with that workbook or any of the objects contained in it, we just need a
reference to the memory location where those objects are stored. We don't need
to create them again.
The following lines of code first declare a Worksheet object variable and then set
it to the first worksheet of the workbook linked to variable wbk
Dim wks As Worksheet

Set wks = wbk.worksheets(1)

We will see more about this approach in chapters where we see techniques of
writing macros.
2. A new object would have to be created
This scenario can be handled with two approaches depending on the type of
object we are dealing with.
v Create an object with a visual Basic declaration
This is where we use the ‘New’ keyword.
Here is an example with PowerPoint. The ‘Application’ object is the main object
that contains all other objects required for automating PowerPoint. We will see
the details in Chapter 16 “Working with MS Powerpoint”.
Dim pptapp As PowerPoint.Application

Set pptapp = New PowerPoint.Application

This same thing can be done at the time of declaration as well. The following
line of code shows how
Dim pptapp As New PowerPoint.Application

1. This approach is used mainly to create the top level objects required for
automating another application. In our example, we are automating the MS
PowerPoint application.
2. We need to do some setup in the Visual Basic Editor in order to use this
approach. We will cover details in other chapters when we see techniques for
automating specific applications.
v Use Methods and Properties of some container object
Most of the objects we use in our macros are ‘contained’ in some higher level
object. For example, in case of Excel, there is a top level “Application” object. It
has a property called “Workbooks” which is a collection of all the workbooks
that are open at a given time.
1. Loading an object from disk.
Often objects are stored as files on the computer’s disk. We can load the object
into the macro by opening the file in our macro. We can use the “Open” method
of the Workbooks collection. Here is the code
Dim wbk As Workbook

Set wbk = Workbooks.Open(“C:\Reports\Book1.xlsx”)

2. Creating a new object
The Workbooks collection provides another method, named “Add”, that allows
us to create a new blank workbook. Here is the code for doing this
Dim wbk As Workbook

Set wbk = Workbooks.Add

C.4.2 Releasing Objects


Once we are done working with an object and don't want our reference variable
to be linked to it, we can unset the reference variable by setting it to ‘Nothing’.
Set wbk = Nothing
The keyword ‘Nothing’ represents an object variable (reference) that does not
refer to any actual obect.
There is one important use of this keyword. To check if an object is initialized
(the technical term is instantiated) or not, compare it with ‘Nothing’ this is a
good practice to avoid errors in our program.
If (obj Is Nothing) then
‘//**…code to take appropriate action **//
End If

We will see specific use of such code in several chapters in this book.
C.5 Operators
We will discuss about operators without delving too deep into technical details
covering just enough to allow us to write macros.
An operator:
1. Is a programming instruction
2. Is usually represented by a single character symbol (some operators have up
to three characters)
3. Works on two variables or expressions (of the same data type). We will call
these arguments. An exception is a logical NOT operator that works on a
single argument.
4. Produces a result (not necessarily of the same datatype as the arguments)
Depending on the function the operators perform they can be classified into the
following types
A) Arithmetic operators
These are used to perform arithmetic operations. Most of these we have seen
during highschool. Syntax for using these is as follows:
Result= number1 <operator> number2

The arguments ’number1’ and ’number2’ are numeric variables or expressions.


The following table lists the result produced by the different operators.

Operator Result Comments


^ number1 raised to power of number2 number1 can be negative only
when number2 is a whole number
* number1 multiplied by number2
/ number1 divided by number2 error occurs if number2 is 0
+ number1 added to number2
- number2 subtracted from number1
Mod Remainder when number 1 is divided by Decimal numbers are rounded to whole
number2 numbers before dividing. Result is always a
whole number

For all the arithmetic operators if either of the arguments is NULL, the result is
NULL.

B) Comparison operators
As the name suggests, these operators as used to compare numeric arguments.
However, the result is a Boolean value. Syntax for using these is as follows:
Result= number1 <operator> number2

The arguments ’number1’ and ’number2’ are numeric variables or expressions.


The following table lists the result produced by the different operators

Operator Returns True if Returns False if


< (Less than) number1<number2 number1>= number2
<= (Less than or equal to) number1<= number2 number1>number2
> (Greater than) number1>number2 number1<= number2
>= (Greater than or equal
number1>= number2 number1<number2
to)
= (Equal to) number1 = number2 number1<>number2
<> (Not equal to) number1<>number2 number1 = number2

If either of the arguments is NULL, the result is NULL.
C) Logical operators
These are used to perform logical operations. The arguments in this case are of
Boolean variables or expressions. Syntax for using these operators is as follows:
Result= expression1 <operator> expression2

The arguments ’expression1’ and ’expression2’ are logical (Boolean) variables


or expressions. They should result in True or False values. The following table
lists the result produced by the different operators
Operator Returns TRUE if

AND Only if both arguments are true


OR At least one of the two arguments is true
XOR Only if one argmentis true and the other is also
NOT The argument is FALSE. NOT takes a single argument and
produces a boolean value opposite to that of the argument.
D) String concatenation operator
This operator is used to join two string arguments. It is denoted by “ & “. In the
code that follows, the variable Result will contain the string “Hello World”
Result = “Hello” & “World”
C.6 Code Groupings
In this section we will discuss about modules, sub-procedures and functions.
These are ways available to us for organizing the code of our macro. In fact, a
macro or program can be considered to be a combination of modules, sub-
procedures and functions.
They provide the following advantages:
1. Organize the code by functionality.
All the code that deals with bringing in and sending out data from our Excel file
can be placed in a separate module. All the code that processes that data can be
placed in another module. In the input*output module, the code that reads text
files can be placed in one sub procedure the code that sends data to a PowerPoint
file can be placed in another sub-procedure.
Organizing code this way helps us at the time of editing the code for changes and
bug fixing.
2. Selective execution of code.
We may not want our entire code to run every time. Some code would run only
when some condition is met.
For example, we give an option to the user to save output to MS PowerPoint or
MS Word. Based on what the user selects we can call the relevant sub-procedure
to save the output.
In this section we will see how we can create our own modules and procedures.
However, Visual Basic and VBA objects have several built in procedures that are
very useful for creating solutions. These are explored in detail in various
chapters throughout the book.

C.6.1 Modules
Modules are like the topmost containers of code. They contain some variable
declaration and functions and sub procedures. A module is basically a collection
of functions and sub procedures. Module can contain declararion for variables
that are shared between functions and sub procedures in that module.
We cannot write code directly inside a module. We should first create a function
or sub procedure that will contain the code.
Creating and naming a module:
Right click in the Project Explorer window and select ‘Insert’ in the pop-up
menu and then select ‘Module’.
If no other module exists in the project, then first a separate folder named
‘Modules’ is created in the Project Explorer. The new module is added in the
Modules folder.
To set the name of the module, click and select the module. In the Properties
Window, type the new name in the space labelled “Name”.

C.6.2 Sub-procedures
A sub procedure is a set of code or program instructions that together achieve a
single purpose. The group of code is given a name which can be used to exeute
the code written therein. Going forward we will use the term ‘Sub’ instead of
‘Sub-procedure’.
A Sub is created using the ‘Sub – End Sub’ code block as follows:
Sub <proc name>(arglist)
‘//** code statements **//
Exit Sub

‘//** code statements **//
End Sub

Such a block of code is called a sub’s “Definition”.


’<proc name>’ is a name of the Sub and the rules for naming are similar to that
of naming variables.
’arglist’ is a list of parameters that will produce different results with the same
lines of code. These parameters are called ‘Arguments’ of the Sub. We will
discuss ’arglist’ in a much greater detail in a short while.
‘Exit Sub’ is a statement that will stop the execution of the sub and will transfer
the control of the macro to a point where the Sub was triggered. This statement
is useful to avoid errors. The validity of arguments as well as intermediate
results produced in the Sub can be checked (usually in an If-Then-Else block
that we will see in a later section) and, in case of issues we can exit the Sub.
The name of the sub along with the list of arguments is often called the
“Signature” if the sub. For the matter that follows consider Sub that allows us to
put values in cells of a worksheet. Here is the code.



Sub put_in_sheet(ByVal strwksname As String, _
ByVal nrow As Long,ByVal ncol As Integer, _
ByVal varValue As Variant)

Thisworkbook.Worksheets(strwksname).Cells(nrow,ncol).Value = _
varvalue
strwksname= "changed sheet name"
End Sub

Notice the following:
a) The Sub takes 4 arguments of different datatypes
b) Value of one of the arguments is changed inside the Sub.
c) Each argument is specified in the following format

ByVal <arg name> As <datatype>

d) Name of the arguments follow the same rules as those of variables
e) Multiple arguments are separated by commas
f)
v Executing code in a Sub
There are three ways of executing the code in a Sub. Let’s look at them one by
one
1. From within another Sub
The act of passing appropriate parameters to a Sub and triggering the execution
of its code is known as “Calling” a Sub. This is done using the “Call” keyword
followed by a space and the concerned Sub’s name. As an example, let us create
a sub procedure that will use the functionality that our earlier procedure
provides. Here is the code:


Sub stub_proc()
Dim strSheetName as String
strSheetName = "Sheet1"
Debug.Print strSheetName
Call put_in_sheet(strSheetName, 4,4,"hello world")
Debug.Print strSheetName
End Sub

Notice the “Call” statement that triggers the Sub we wrote earlier.
2. Manually from the Excel interface
This technique works when the concerned Sub does not take any arguments.
In the Developer ribbon, click on “Macros”. This brings up a dialog box
showing a list of all the Subscreated in all open workbooks. The dialog box is
shown in figure C.6.2a.

Fig. C.6.2a List of Sub procedures

Select the Sub to execute and click on Run.


When this dialog box first appears, it shows a list of all Sub procedures created
in all the open workbooks. To display the macros created only in a specific
workbook, click on the arrow of the dropdown labelled as “Macros in” (shown
by 1 in the figure)
3. Manually from the Visual Basic Editor interface
This technique works when the concerned Sub does not take any arguments.
Firstly, in the VBE, open the definition of the Sub and click inside it. The
Procedure box of the module should show the name of the Sub.

Fig. C.6.2b Executing a Sub manually in VBE

Click on the “Run” menu and click on “Run Sub”. Alternatively, press F5
v Arguments of a Sub
A sub procedure can take input variables which can be used to control the
actions of the sub procedure. For example, if the sub reads data from a text file
into Excel, then the input can be a string having the path of the file. This way we
can use the same procedure to read different files having the same format. Hence
the same sub can produce different results based on the parameters it receives.
The parameters themselves are called “Arguments” and the act of specifying
parameters for a sub is called “Passing the Arguments”. Arguments act as named
variables inside the code of the sub.
Ø By Value
In the sub ’put_in_sheet’ that we created in the beginning of this section, the first
argument in the definition is ’strwksname’. The name of the argument is
preceeded by the word “ByVal”. This term indicates that the argument is passed
by value.
In the calling sub ’stub_proc’ the name of a worksheet is held in a string variable.
This variable is passed to ’put_in_sheet’. Since the argument is passed by value,
a new location is created in the computer’s memory for a string variable. It is
labelled as ’strwksname’ and the value held in ’strSheetName’ is now copied
there. So now there are two locations in memory that hold the same value.

If we execute the ’stub_proc’, we will see that the string "Sheet1" is printed twice.
This is because in sub ’put_in_sheet’ the value of the copy is updated and the
original string stays unaffected.

Ø By Reference
Another way to pass an argument to a sub is by reference. In this case, the name
of the argument is preceeded by the word “ByRef”. The definition of the sub
with the first argument passed by reference will look as follows:

Sub put_in_sheet(ByRef strwksname As String, _
ByVal nrow As Long, _
ByVal ncol As Integer, _
ByVal varValue As Variant)
‘// ** code for the sub **//
End Sub

Notice that in the same sub we can pass some arguments by value and others by
reference.
Passing by reference means we create a new reference variable that points to the
same memory location. No new string variable is created. So if a variable is
passed by reference to any sub, that sub is able to modify the passed variable.
Let us consider one example.
If we execute our ’stub_proc’ now we will see that “Sheet1” is printed once and
"Changed sheet name" is printed next after the procedure is called. This is
because ’put_in_sheet’ is able to change the variable ’strwksname’ even if it is
not exclusively declared in that sub.
It is important to note that object variables and array variables should always be
passed by reference. So whenever we create a Sub that takes workbooks or
worksheets or presentation files and so on as arguments, they have to be ByRef.
Ø Optional arguments and default values
Many of the subs and functions that are built into Visual Basic and Excel can
have arguments that are optional. We will see examples in almost all chapters.
When calling these procedures, we may choose not to specify any value for such
arguments.
Improper use of optional arguments can lead to unpredictable effects so we will
not delve into creating our own subs/functions with optional arguments.
However, we should be aware that whenever a procedure uses optional
argument, there is a default value assigned to that argument. In case we don’t
specify a value for that argument when calling the procedure, the default value is
used.
v Exiting a Sub before completion

A) From inside the macro based on conditions
The statements written inside a sub are executed in the order in which they are
written. However, there is a way to stop execution if some unwanted condition
develops. this is done with the “Exit Sub” statement. Any number of Exit Sub
statements can occur in a Sub, but these should all be based on some conditional
check.
Once an “Exit Sub” is encountered, no subsequent statement is executed. Also,
we should undo as many changes as possible before we decide to exit the sub
before completion. In our Sub we can have statements as follows
If (nrow<1) Or (ncol<1) Then
MsgBox(“Row or Column numbers should be greater than 0”)
Exit Sub
End If

The function ’MsgBox’ is a built in Visual Basic function and is described in
detail in Chapter 8.

B) Manually using mouse or keyboard
Sometimes we may want to manually exit a macro in the middle of
execution. Examples of such conditions are:
· The macro stops responding and simply hangs up either because a file to
be processed is too large and can't be opened or it is waiting to connect to a
server over a slow connection.
· An error in the programming logic has caused an infinite loop (see section
C.9.2).
1. To break the running VBA program, do one of the following:
· On the “Run” menu, click “Break”.

· Press Ctrl + Break keys on the keyboard.

If a Break key is not available on the computer’s, try Ctrl + Pause


2. A dialog box is displayed, and the program is suspended.
Select one of the following:
· Continue: Resumes the execution of the program.
· End: Terminates the VBA program.
· Debug: Displays the line of code where the run-time error occurs.
· Help: Brings up VBA Online Help.
If step 1 does not work, the only option (in Windows) is to go to the Task
Manager, select Excel in the Applications tab, and click on “End Task”. Mac
users would have to find a similar alternative.

C.6.3 Function procedure


A function procedure or function, is a set of instructions similar to a sub. The
difference is that a function has to return something to the calling procedure.
Here is the code for creating a function:
Function get_from_sheet(ByRef strwksname As String, _
ByVal nrow As Long, ByVal ncol As Integer)As Variant

‘//** code statements **//
Exit Function

‘//** code statements **//
get_from_sheet = _
Thisworkbook.Worksheets(strwksname).Cells(nrow,ncol).Value
End Function

The concepts about arguments that we saw for a Sub procedure apply to a
Function procedure as well.

Of special importance is the one line where we have the name of the function on
the left of an “=” sign and it is being assigned a value. This means the value on
the right of the = sign will be returned back to the calling procedure. Such a line
should be immediately followed by an “End Function” or an “Exit Function”
line.
In the following example we have a sub that calls a function. Notice how the
function can appear on the right hand side when assigning value to a variable.
Sub TestFunction()
Dim varFromSheet As Variant

varFromSheet = get_from_sheet(“Sheet1” 2,5)
End Sub

Important note:
If the function returns an object, then the word “Set” has to be used when
returning and assigining. Consider the function and sub shown in the code
below:


Function MyFunction(ByVal SheetName As String) As Worksheet
‘//** code statements **//
Set MyFunction = ThisWorkbook.Worksheets(SheetName)
End function

Sub TestWks()
Dim wks As Worksheet
‘//** code statements **//
Set wks = MyFunction(“Sheet1”)
End Sub

C.6.4 Share Subs, Functions & Variables across


modules
Calling procedures from another module
Sub and function procedures defined in one module can be called from some
other module.
Suppose we have a module named ’modTxt’ that has procedures to read and
process data from text files. We have another module ’modMain’ that contains
the main processing code for our macro. The following code is written in a sub
in ’modMain’ that will call a function in module ’modTxt’ and also pass a string
argument and receive an integer value returned by the function.
Notice the syntax “ModuleName.ProcedureName”
Sub ProcMain()
`//** Other code **//
mNumFiles = modTxt.ReadInputFiles(strFolderPath)
End Sub

Sharing variables between procedures inside a module


At times we may want to share variables between different procedures inside the
same module. Such variables are called ‘Module level variables’ and can be
declared with the Dim statement but, they should be declared outside any Sub or
Function procedure. They should be declared ahead of any prodecure.
Figure C.6.4 shows four variables declared at the module level. It also shows one
Sub and one function that use these variables.
When using this technique, we need to ensure that we don’t declare any variable
with the same name as a module level variable inside a procedure.
Variables that are declared inside any procedure are called Local variables for
that Sub or Function procedure. In figure C.6.4, the Sub ’MainSub’ has a local
variable named ’nMultiplier’.

Fig C.6.4a Module level variables


The point to note is that Local variables lose their values and the space they
occupy in the computer memory is released as soon as the sub/function ends.
Meaning, if we assign a value to ’nMultiplier’, that value will be lost when’
MainSub’ ends.
The next time we call ’MainSub’, a new variable called ’nMultiplier’ is created
and again a value has to be assigned to it.

Fig C.6.4b Module level variables retain their values between function calls

The useful feature about Module level variables is that they get created when
any Macro execution begins (remember that a macro is a collection of Sub and
Function procedures) and they don’t lose their values even if program control
passes out of the module in which they are declared. Their values are lost only
when macro execution stops and program control passes to Excel and the user.
Suppose we have another module named “Module2”, which contains a sub that
calls the subs in Module1. The module, its code and its output are shown in
figure C.6.4b.
’MainSub’ assigns the values, ’TestFunction’ adds them, the output is printed by
sub ’test2’.
C.7 Many items of the same type – Arrays &
Collections
Many times in a program, we require a large number of variables (or objects) of
the same type in order to perform the same type of operations on each them.
For example, suppose we are creating a list of 50 students in a class. We need 50
string variables but, should we create 50 strings? We will have to use 50
different variable names and call some function 50 times. What if tomorrow one
more student gets added? At how many places in our code should we make
changes?
What if we could have another sort of variable that could hold the 50 strings and
from which we can extract any one string as per our requirement? We can have
only one name. As for calling functions or subs, we could pass this single
variable as argument, and the function could pull out the names one by one and
work on them.
Visual Basic provides two types of variables that allow us to work with a bigger
set of variables. Let’s look at them one by one.
v Arrays
An array is a set of sequentially ordered elements having the same primitive data
type. Each element of an array has a unique identifying index number. Changes
made to one element of an array don't affect the other elements.
There are various ways of declaring and initialiazing arrays. We will focus on
two major approaches that relieve us of keeping track of the first index. This is
helpful if we are augmenting code written by someone else.
We will cover the basic case here. The other approaches have been covered in
the main sections of the book. A reference is provided to the concerned section.

First case:
This is the case when we know the total number of elements we want in our
array.

‘//** Array of 11 long integers, lower index 0 **//
Dim arrLong_0(0 To 10) As Long

‘//** Array of 10 long integers, lower index 1 **//
Dim arrLong_1(1 To 10) As Long

Values have to be assigned individually to each element as follows

arrLong_1(1) = 15

arrLong_1(2) = 200
‘//** We would require 10 such lines. **//

In the Dim statements, numbers in the brackets are the range of the arrays
“index”. Index is the relative position of an element in the array. so in the first
array, the first element is arrLong_0(0), the third one is at arrLong_0(3).
The smaller value of the Index is called the “Lower Bound” of the array. The
larger value is called “Upper Bound” of the array.
If the number of elements is known along with the individual values of the
elements we can use the “Array()” function. This function is described in detail
in section 8.1 “Functions for Arrays.
Second Case:
The second case is more important for us when writing macros in Excel. In this
case we don’t know the length or the contents of the array. The array is read
from an Excel worksheet. This method is described in detail in section 3.6
“Using VB Arrays with worksheet Range”
Ø Dimensions of an array:
One thing to note about the arrays we have created so far is that they are one
dimensional. They can be thought of as a variables of similar data type arranged
in a row. This is shown in figure C.7a


A single Array: An array of arrays
(58, 6, 76, 45, 89) ( (45,2),
(12,64),
(78,63))
Fig C.7b Two-Dimensional array of 6 integers
Fig C.7a One-Dimensional array of 5 integers

However, arrays can be two dimensional. in that case, we can think of them as
variables arranged in a table as shown in figure C.7b. Although this concept is
not frequently required when writing Excel macros, it is useful to know it
especially when we plan to create arrays by reading values from cells on a
worksheet.
In such a case, to access a single element we require indices of both the
dimensions. Each element is like a cell in a table. Such arrays are called two
dimensional arrays and these can be declared as follows
Dim arr2DLng(1 To 10, 1 To 20) as long

The first set of indixes is for the first dimension or the rows of the table. The
second set (1 to 20) is for the second timension or the columns of the table. we
can assign value to a particular element as follows
arr2DLng(2,5)= 200
Although it helps to visualize a 2-Dimensional array as a table, in reality it is a
1-Dimensional array where each element is another 1-Dimensional array. Hence,
if the array shown in figure C.7b is named ’arr2DTest’ then, assuming the first
index is 1,
‘//** will be the array (45,2) **//
arr1D = arr2DTest(1)
‘//** will be the integer (45) **//
arr1D(1)


v Collections
A collection is a special type of object that can hold a large number of other
objects all of which are of the same type. Some examples are
1. The collection of all worksheets in an Excel workbook – aptly named the
‘Worksheets’ collection.
2. The collection of all open Excel workbooks – called the ‘Workbooks’
collection.
Since collections are special type of objects we will not create any collection of
our own. However, we will be working with collections that are already provided
by the applications we are trying to automate. The Worksheets and Workbooks
collections mentioned earlier are available in Excel. Every PowerPoint file has a
Slides collection which we will use when sending data automatically to
PowerPoint.
Every collection, regardless of the objects it contains, always has one property
and one method readily available. Let’s see what those are
Ø Count Property: This property returns a long integer value that represents the
total number of items held in the collection. The syntax for retrieving this
property is
<collection variable name>.Count
Ø Add Method: This method allows us to add a new object to the collection. Its
behavior differs slightly based on how the collection has been created. We will
deal with this method when we look at the individual collections.
Salient points about collections:
a. Collections, themselves being objects, can become properties of other objects.
The Worksheets collection is a property of a Workbook object.
b. Within a collection, objects are stored in an ordered manner and are
numbered. The first object is at position 1. For example, the code below shows
how to access the first and fifth worksheets in a workbook set to the wbk
variable.
wbk.Worksheets(1) and wbk.Worksheets(5)

c. Collections in MS Office allow us to set names for many of the objects and
these can be retrieved from their respective collections using their names. This
is good news because we don’t have to remember the specific location of the
objects that we are interested in. For example, a worksheet named
“DataSheet” can be retrieved with the following line of code.

Wbk.Worksheets(“DataSheet”)

v Passing Arrays to Subs and Functions
Let us look at code examples for passing array variables to Subs and functions as
arguments. First, consider the function ’arrFunTest1’ as shown.

Function arrFunTest1(ByVal strPrefix As String, _
ByRef arrNums() As Integer) As String
Dim dblSum As Double
dblSum = Application.WorksheetFunction.Sum(arrNums)
arrFunTest2 = strPrefix & dblSum
End Function

Notice the second argument. The empty parentheses after the argument name
specify that the arugment passed will be an array. We do not need to know the
length or dimensions of the array in case it is a one dimensional array.

This code also illustrates how functions available on the worksheet can be used
inside macros. The WorksheetFunction property is discussed in a bit more detail
in section B.2.1

The following sub calls the function ’arrFunTest1’ and passes an integer array as
one of the arguments.

Sub arrtest()
Dim arrvar(1 To 4) As Integer
For i = 1 To 4 Step 1
arrvar(i) = 2 * i
Next i
Debug.Print arrFunTest1("Testing Arr passing: ", arrInt)
End Sub

When we create our arrays by reading values from a range on Excel worksheet,
the array created is always 2-dimensional and is of Variant datatype. In such
cases, the function arrFunTest2 will be more useful to us. This type of function is
also required when the argument passed is an array of multiple dimension even if
the datatype is not variant.

Function arrFunTest2(ByVal strPrefix As String, _
ByRef arrNums As Variant) As String
Dim dblSum As Double
dblSum = Application.WorksheetFunction.Sum(arrNums)
arrFunTest = strPrefix & dblSum
End Function


The following sub calls the function ’arrFunTest2’ and passes a variant array as
one of the arguments.
Sub arrtest()
Dim arrtest As Variant
arrtest = Array(1, 2, 3, 4, 5)
Debug.Print arrFunTest2("Testing Arr passing: ", arrvar)
End Sub


v Passing Collections to Subs and Functions
Passing collections to procedures is pretty straight forward expecially when
writing macros because the type of objects in the collection is always explicitly
mentioned. Let’s look at an example. The following sub takes a collection of
Worksheets and prints out their names
Sub PrintSheetNames(ByRef wksColl As Worksheets)
Dim wks As Worksheet
For Each wks in wksColl
Debug.Print wks.Name
Next wks
End Sub

C.8 Choosing a path to follow – Decision structures
At several points in our macro, a decision is required about what to do next
based on certain conditions that develop when macro is executed. Most of the
times the conditions are determined by value of one or more variables. For
example:
1. Macro prompts the user to select a file. If user chooses an Excel file, we
should open it using Excel. But, if a text file is chosen, we should use another
method to work on the file. If it is neither of these, the macro should display a
message and just stop.

In this case, the variable would be a string holding the file extension that is
retrieved from the path of the selected file.

2. While checking payments from customers one at a time, if payment from any
customer has been pending beyond a due date, we should mark the row having
the customer details in red and copy it to another sheet.

In this case the variable would be an Integer, holding value of difference
between the due date and current date for the concerned customer.
The features of programming languages that allow us to build decision making
into our code are collectively called Decision Structures.
In this section we examine the two most important Decision Structures available
in any programming language.

C.8.1 If-Then-Else
The syntax of this decision structure is as follows:

If condition-1 Then
[statements] – Group 1

[ElseIf condition-2 Then


[elseifstatements] ... – Group 2

[ElseIf condition-3 Then


[elseifstatements] ... – Group 3
[Else
[elsestatements]]– Group 4

End If

Such a group of statements is called an ‘IF-block’. If we start with an ‘If’, then


we should have the matching ‘End If’ and ‘Then’. It is Ok if there are no ‘ElseIf’
or ‘Else’ statements.
Each of the ’condition’ expressions are string, boolean or numeric expressions
that when worked out , or ‘evaluated’, at that point in the program, result in
either a True or False value.
A single ’condition’ could be a combination of multiple expressions (numeric or
string) each of which individually evaluates to True or False and are then
combined using Logical operators (And, Or, Not). We illustrate this in our code
example.
The code execution happens as follows:
1. If ’condition-1’ evaluates to ‘True’, the code between the first ‘Then’ up to the
next ‘ElseIF’ ie Group 1 is executed. All others are ignored.
2. If ’condition-1’ evaluates to ‘False’ but ’condition-2’ evaluates to ‘True’, the
code between the second ‘Then’ up to the next ‘ElseIF’ ie Group 2 is executed
and all others are ignored.

3. If all 3 conditions evaluate to ‘False’ the code in Group 4 is executed.

4. After that, our macro works on the code immediately following ‘End If’.
Usually the ‘ElseIf’ lines are not used. And a typical ‘If’ block usually only has
the ‘If-Then-Else-Endif’ as follows,
If condition Then
‘//** Code to execute if the condition is true **//
Else
‘//** Code to be executed otherwise **//
End If


It is also normal to have just the “If-End If” part if the situation so demands
If condition Then
‘//Code to execute if the condition is true
End If


Now, recall the example of file types mentioned at the beginning of this section.
The code for working with the appropriate files is shown below. Don’t worry
about the term ‘TextStream’ it is covered in detail in section 14.3 “Reading &
writing text files”.
Dim strFileExt As String
If ((strFileExt = “.xlsx”) Or (strFileExt = “.xlsx”)) Then
‘//** Use Excel to open this file **//
ElseIf (strFileExt = “.txt”) Then
‘//** Open this file as a TextStream **//
Else
‘//** Display a message to the user to select the proper file and then stop. **//
MsgBox “Please select either a Text or an Excel file”
Exit Sub
End If
If we are working with an object datatype, we have to ensure that the object is
instantiated before we can work with it. In that case, the following construct of
IF comes in handy (notice the Is operator and the Nothing keyword)
If obj Is Nothing Then
‘//** Code to execute if the condition is true **//
End If

C.8.2 Select Case


This is another decision structure that allows us to execute code when a variable
takes one of many possible values. Its syntax is as follows:
Select Case testexpression
Case expression-1
Group-1...
Case expression-2
Group-2 ...
Case expression-3
Group-3...
Case Else
else Group
End Select

Such a group of statements is called an ‘Select-Case block’.
The ’testexpression’ is any Numeric or String expression. It is not a Logical
expression so it does not evaluate to True or False (seasoned programmers can
overcome this limitation, but we will stick to the standard methods.)
expression-1,2,3, are other expressions of the same type as the ’testexpression’.
Here is how the code works:
1. The ’testexpression’ is evaluated.
2. The result is compared to result of ’expression-1’ after evaluating it. If the
results match, the statements of Group-1 are executed. And program control
moves out of the Select-Case block.

3. If results of ’testexpression’ don’t match that of ’expression-1’ then,
’expression-2’ is evaluated. If the results match, the statements of Group-1 are
executed. And program control moves out of the Select-Case block.

4. If a match is not found in any of the expressions, then the statements of “Else”
group are executed.
We now recast our example of filetypes in form of Select-Case block.
Select CasestrFileExt
Case “.xlsx” , “.xls”
‘//** Use Excel to open this file **//

Case “.txt”
‘//** Open this file as a TextStream **//

Case Else
MsgBox “Please select either a Text or an Excel file”
Exit Sub
End Select

C.9 Doing something again and again – Loops
Often we have to carry out the same set of operations again and again with
different variables or inputs.
An example would be, start at row number 2 on a worksheet. Check the first
column in each row, if the third character is ‘X’ change it to ‘Y’. Check all rows
till row number 500.
Visual basic provides the concept of ‘Loop’ to achieve this purpose. Let us look
at the two main loops available in Visual Basic. For a Loop, each time the set of
operations is carried out, the loop is said to have performed an iteration.

C.9.1 For Loop


A “For” loop is used when we know the number of iterations in advance. The
loop’s execution is controlled by a variable called a “Counter” for which we
have to specify a start value and an end value and the increment (or decrement)
for each iteration. The full syntax is as follows:
For counter = start To end [Step step]
[statements]
[Exit For]
[statements]
Next [counter]

The ‘For loop’ is useful when the items we are working on are themselves
numbered. For example, elements of an array (in that case the start and end
values would be the lower and upper bounds of the array) or rows on a
worksheet (we could go from row 1 to row 100).
Exit For: This statement gives us a way to stop executing the loop and jump out
of it before completing all the iterations. It can be useful in conditions when we
don’t know where in between our start and stop values our purpose will be
served. So we test for a condition at each iteration using an ‘If’ block. When that
condition is met, we use “Exit For” inside the If-block to move out of the loop.
We now consider some examples. For this example, we will use some objects in
an Excel workbook. We will be exploring these objects in details in the chapter
on VBA. However, for these examples consider the following list
· ’Wks’ object that represents a worksheet in an Excel workbook.

· ’rows’ collection that represents a set of all rows on a worksheet. An
individual row is obtained using the row number. For example, the 5th row is
obtained with ’rows(5)’

· ’cells’ collection of all cells on a worksheet. An individual row is obtained
using its row number and column number. So, cell C5, in 3rd column and 5th
row, is obtained with ’cells(5,3)’

1. This example goes from row 2 to row 100 and puts the row number in the
first column of the row.


For rw = 2 To 100 Step 1

wks.Cells(rw,1) = rw
Next rw

2. This example goes does the same thing as the example above, except that it
goes from 100 to 2. Notice how the value of ‘Step’ is negative.
For rw = 100 To 2 Step -1

wks.Cells(rw,1) = rw
Next rw

3. In this example every row that is a multiple of 5 will have phrase “Multiple
of 5” in the cell in colum A.
For r = 2 To 100 Step 5
If (r Mod 5)=0 then
wks.Cells(r,1) = “Multiple of 5”
Endif
Next r

Notice the use of Mod operator on the counter itself
4. This example is same as example 3 with one difference. If in any row, the
first cell (in column A) contains the word “Total”, we don’t go any further.
The program control exits the For loop.
For r = 2 To 100 Step 1
If (wks.Cells(r,1).Value=”Total”) then
Exit For
Endif

If (r Mod 5)=0 then
wks.Cells(r,1) = “Multiple of 5”
Endif
Next r

Notice that the Mod operator is used on the counter variable (r) itself to check
the value that ‘r’ takes. However, we won’t modify the value of ‘rw’ in any
way.
It is easy to note that we can have ‘Exit For’ inside any type of check done by
the If block. For example, in the If block we can check for an object reference
using the ‘Nothing’ keyword.
5. In this final example we see how handy the ‘For Loop’ can be when we have
to work with each individual element in an array. Suppose we have an array
named ‘myArray’.
The array element at location ‘i’ is myArray(i). We can proceed as follows:

Dim intStart, intEnd As Integer


intStart = LBound(myArray)
intEnd = UBound(myArray)
For r = intStartTointEndStep1
‘//** do something with myArray(r) **//
Next r

C.9.2 While Loop


The ‘While’ loop is another important looping structure. Unlike the ‘For’ loop
that depends on value of a counter variable, the ‘While’ loop can be controlled
with either value of a variable or that of an expression.
While condition
‘// Statements to execute
Wend
The loop begins with the ‘While’ keyword and ends with Wend (‘While end’).
These two lines and all the statements between them are collectively called a
‘While’ block.
’Condition’ is any String or Numeric expression that evaluates to either True or
False.
As long as ’Condition’ is true, the loop keeps working. This also means that:
1. For the loop to get started, the ’Condition’ has to be true in the first
iteartion.

2. At some point in time, the statements inside the While block have to make
the ’Condition’ ‘False’. Otherwise, ’Condition’ will be ‘True’ forever and
our macro will keep going in circles. This condition is called an ‘Infinite
Loop’. The upcoming examples will make this clear.
Here is how the code works:
1. Our macro is working its way through the code lines, and suddenly encounters
the line with ‘While’.

2. ’Condition’ expression is evaluated.

3. If ’Condition’ evaluates to True, program control enters the While block and
executes the statements one by one till it hits ‘Wend’.

4. ’Condition’ is evaluated once again and, if found ‘True’, the entire group of
statements in the block is executed again.



Now, we will recast our examples from section C.9.1 on “For Loops” with While
loops.
1. In each row, insert the row number in the first cell (Column A) for rows 2 to
100.
Dim r As Integer
r = 2
While (r < 100)
wks.Cells(r,1).Value = r
r =r + 1
Wend

2. Doing the same thing as 1. from row 100 to row 2.
Dim r As Integer
r = 100
While (r > 2)
wks.Cells(r,1).Value = r
r = r - 1
Wend

3. In every row that is a multiple of 5, insert the row number in the first cell.
r = 2
While (r < 100)
If (r Mod 5)=0 then
wks.Cells(r,1).Value = r
Endif
r =r + 1
Wend



Let us see some important differences between the two loops we have seen so
far.
For Loop While Loop
Control Controlled with a numeric counter Controlled by a Boolean expression.
variable. Can be a Boolean variable, or a string
or numeric expression that evaluates to
True or False.
Start & End Specified in the starting line of the loop’s Start Value: specified before the
body. beginning of the loop. Outside the
loop’s body.
End Value: specified in the test
expression.
Step Specified in the starting line of the loop’s As a statement within the loop’s body.
body.
Conditional Available with ‘Exit For’ statement. No standard method available, have to
Exit modify the test expression to false
within loop’s body


There is nothing like ‘Exit For’
We must note that unlike “Exit For” in “For Loop”, there is nothing available for
a While loop to stop its execution based on some test condition. However, we
can work around this limitation by declaring a Boolean variable whose value
depends on a test condition. Then we just use this Boolean variable in place of
the test condition.
Consider example 4 from the section on ‘For Loop’. We want to exit the loop
when we get the word “Total” in column B for a row. But at the same time, we
just want to look at rows 2 to 100, and place some value in every row that is a
multiple of 5.
Dim bKeepLooping As Boolean
Dim r As Integer
r = 2
‘//** Initially set the Boolean variable to TRUE **//
bKeepLooping = True
While (bKeepLooping)
If (r Mod 5)=0 Then
wks.Cells(r,1).Value = r
End If
r =r + 1
If (r >100 Or wks.Cells(r,2).Value=”Total”) Then
‘//** No more iterations will happen **//
bKeepLooping = False
End If
Wend

C.9.3 For Each Loop


This loop is useful when we want to loop through all members of a collection.
The syntax is as follows:
For Each <objVar> In <object collection>
‘//work with the object

Next <objVar>

’<objVar>’ is an object variable of the same type as the members of the
collection.
Let’s look at one example. The following loop prints the name of each worksheet
in the current workbook.
Dim wks As Worksheet
For Each wks In ThisWorkbook.Worksheets

Debug.Print wks.Name
Next wks
’ThisWorkbook’ is the object that represents the workbook in which the macro is
written. ’Worksheets’ is a collection of all worksheets in a workbook. Each
worksheet has a property called “Name” that holds the name of worksheet.
C.10 Multiple operations on the same object – With
Block
Sometimes, we may want to do several different operations on a single object
but using different properties and methods. The ‘With….End With’ block allows
us to club processing into a single block and reduces the amount of typing
required.
The following code shows how 3 operations are done on a single worksheet.
With ThisWorkbook.Worksheets(“Sheet 1”)
.Name = “First Sheet:
.Range(“A1”).Value = “First Column/First Row”
.Range(“B2”).Formula = “=Left(A1,5)”
End With
C.11 Miscellaneous constants
Here sare some predefined string constants provided by Visual Basic that we can
use in our macros when we want to form strings by joining different characters
or other strings.
The assembled string can be written to a file or displayed on the screen in a
message box or a form. There are a good number of constants available suitable
for different tasks, but we will need only two. Here they are
1. vbCrLf : Produces the same effect as pressing the ‘Enter’ key and starting a
new line.
2. vbTab : Produces the same effect as pressing the ‘Tab’ key.
The code below assembles one such string and displays it in a message box. The
output is shown in figure C.11.
Dim str As String
Str = “Hello” & vbTab & “Stranger.” & vbCrLf & “How are you?”
MsgBox str

Notice the blank space between first two words due to Tab character

Fig C.11 Assembled string displayed in message box.

You might also like