Logo: TechTrax...brought to you by MouseTrax Computing Solutions

Excel VBA: No Experience Necessary - Lesson 3

by Mark Thorpe

A quick recap…
In Lesson 1, we covered the introductory topics of: recording and running a macro, the Visual Basic Editor, the for loop, and referencing a cell. In Lesson 2, we covered variables and data types, Excel objects, properties, and methods, and object collections. In this, Lesson 3, I will touch on debugging, user-defined functions, and using Excel functions in VBA code.

But first, the solution to the exercise at the end of Lesson 2. The challenge was to write a subroutine called "CreateWorkbook" which would automatically create a new workbook and set the names of each worksheet in the new workbook according to names stored in the first sheet of the initial workbook.

You may download the complete solution here: Excel VBA Article 2 Example Solution.xls (in zipped format).

The first step is easy. A new Excel workbook can be created by simply using the Add method of the Workbooks collection:

Example 1a.

Sub CreateWorkbook()
    Workbooks.Add
End Sub

Next we need to determine how many worksheets should be in the new workbook. We can do this by counting how many names are listed in the original workbook (the workbook with the VBA code). Keep in mind that we now have two workbooks open, the original and the new one created by our subroutine. When we added the new workbook, it became the "active" workbook, but since we want to count the names which are stored in the original workbook, we need to reference it as "ThisWorkbook". This signifies that the code is referencing the workbook in which it (the code) is contained.

We are assuming that the names are contained on the first worksheet, so we will use ThisWorkbook.Worksheets(1). You may remember from Lesson 1 that we can find out how many rows are actually used in a worksheet by looking at the "UsedRange" property of the Worksheet object.

Example 1b.

Sub CreateWorkbook()

    Dim iSheetCount As Integer

    Workbooks.Add
    iSheetCount = ThisWorkbook.Worksheets(1).UsedRange.Rows.Count

End Sub

The next step is to set the correct number of worksheets in the new workbook. To do this we will use two separate "while" loops. One will be used if we need to remove worksheets, the second if we need to add worksheets. As I mentioned above, when a new workbook is added, it becomes the active workbook, so we can reference the new workbook using "ActiveWorkbook".

Example 1c.

Sub CreateWorkbook()

    Dim iSheetCount As Integer

    Workbooks.Add
    iSheetCount = ThisWorkbook.Worksheets(1).UsedRange.Rows.Count

    Application.DisplayAlerts = False
    ' If we have too many worksheets, delete one at a time until
    ' we reach the correct number:

    While ActiveWorkbook.Worksheets.Count > iSheetCount
        ActiveWorkbook.Worksheets(1).Delete
    Wend

    ' If we don’t have enough worksheets, add more one at a time
    ' until we reach the correct number:

    While ActiveWorkbook.Worksheets.Count < iSheetCount
        ActiveWorkbook.Worksheets.Add
    Wend


End Sub

The line "Application.DisplayAlerts = False" is handy when you want your VBA code to do something that Excel normally gives you an "Are you sure?" message about when you try to do it by hand. Without this line of code, Excel will display a warning message each time the code tries to delete a worksheet. Setting the DisplayAlerts property of the Application object to False turns off such messages.

Lastly, we need to set the names of the worksheets in the new workbook according to the contents of the original workbook. For this we will use a simple "for" loop, reading each name from ThisWorkbook, and assigning it to the Name property of each Worksheet object in the ActiveWorkbook.

Example 1d.

Sub CreateWorkbook()

    Dim iSheetCount As Integer
    Dim iSheet As Integer

    Workbooks.Add
    iSheetCount = ThisWorkbook.Worksheets(1).UsedRange.Rows.Count

    Application.DisplayAlerts = False
    ' If we have too many worksheets, delete one at a time until
    ' we reach the correct number:

    While ActiveWorkbook.Worksheets.Count > iSheetCount
        ActiveWorkbook.Worksheets(1).Delete
    Wend

    ' If we don’t have enough worksheets, add more one at a time
    ' until we reach the correct number:

    While ActiveWorkbook.Worksheets.Count < iSheetCount
        ActiveWorkbook.Worksheets.Add
    Wend

    For iSheet = 1 To iSheetCount
        ActiveWorkbook.Worksheets(iSheet).Name = _
            ThisWorkbook.Worksheets(1).Cells(iSheet, 1).Value
    Next iSheet


End Sub

Note that we use the iSheet variable as both a reference into the Worksheets collection of the new workbook, and as the row number when reading the names from the original workbook.

You may also notice that an underscore character can be used to split a long line of code over two lines. The underscore must be preceded by a space.

Debugging
When something goes horribly awry in your code, or your code doesn’t produce the results you expect, you can use the Debug and Run menus in the Visual Basic Editor to help locate the source of the problems. Below are some of the most useful commands:

Command

Shortcut Key

Toolbar Button

Description

Run

F5

Executes the current subroutine or function

Toggle Breakpoint

F9

 

Sets or Clears a breakpoint, which pauses execution of the code

Step Into

F8

 

Steps through each line of code (when one subroutine is called from another this will "step into" the called subroutine)

Step Over

Shift + F8

 

Steps through each line of code (when one subroutine is called from another this will "step over" the called subroutine)

Reset

 

Stops execution of your code. You can restart execution only from the beginning.

Break

Ctrl+Break

Temporarily halts execution of your code. You can continue execution from the where it left off.

Quick Watch

Shift + F9

 

When execution is paused, displays the value of the highlighted variable. (You may also view the value of a variable by pausing the cursor over it)

Before you run your code, save your workbook. It doesn’t happen often, but you may inadvertently have written code which is truly evil and will cause Excel to crash. You don’t want to lose your code, diabolical though it may be. Also, pay particular attention to the keyboard shortcut Ctrl+Break. If your code has gone into an infinite loop, or something is taking much longer than you expected, Ctrl+Break is the easiest way to stop it. Once execution has been halted, you can either Reset it, or step through from where it was paused, using F8.

Try using the debugging commands to step through the homework example solution. As you step through line by line, you can actually Alt+Tab (or Alt+F11) between the VBA Editor and Excel to see each worksheet added and each worksheet name as it changes.

Functions
All of the examples that we’ve used so far have been in the form of subroutines, aka macros. In Lesson 1, I mentioned that your VBA code would be in the form of subroutines and functions, but I have yet to define what a function is, or what the difference is between a function or a subroutine.

Well, the main difference is that a function returns a value. In other words, you can call a function from a subroutine (or another function), and use the value that the function passes back. This may be more easily understood with a simple example.

Example 2.

Function UpperFileName() As String
    UpperFileName = UCase(ActiveWorkbook.Name)
End Function

Sub ShowFileName()
    MsgBox UpperFileName()
End Sub

Here we’ve defined a simple function called UpperFileName. We’ve declared the function type (string) in a way similar to how we declare a variable, using "As" followed by a data type. This represents which data type will be returned by the function. In this example, the function returns a string, which it creates by getting the file name of the current active workbook, and converting it to upper case (using the VB function UCase). We return this string value by using the function name as if it were a variable, and setting it equal to the string. The subroutine ShowFileName calls the function UpperFileName and displays the returned value in a message box.

(This may be apparent to you if you’ve written code before, but if you’re one of the brave souls who’s never programmed before and made it this far, you should note that you can call one subroutine or function from another subroutine or function.)

Function and Subroutine Parameters
To really make a worthwhile function, you will need the ability to send values into the function. These values are known as either parameters or arguments. For example, suppose you wanted to write a function which converted a temperature from degrees fahrenheit to degrees celsius. This function would need to take as input a temperature in fahrenheit. You can do this by listing the parameter with its type within the parentheses that follow the function name.

Example 3.

Function Celsius(dFahrenheit As Double) As Double
    Celsius = (dFahrenheit - 32) * (5 / 9)
End Function

Sub ConvertTemperature()
    Dim dtemperatureF As Double
    Dim dtemperatureC As Double
    dtemperatureF = Cells(1, 4).Value
    dtemperatureC = Celsius(dtemperatureF)
    Cells(2, 4).Value = dtemperatureC
End Sub

The function, named Celsius, is declared as a double, meaning that it returns a double-precision floating point value. It takes, as a parameter, a double value called dFahrenheit. It convert dFahrenheit to the corresponding celsius temperature, then returns this value.

The subroutine ConvertTemperature reads a fahrenheit value from cell D1, converts it to celsius using our function, and places the result in cell D2.

Functions can take more than one parameter. Each parameter should be separated by a comma. The function below, for example, can convert a number to either celsius or fahrenheit, depending on the value of the second parameter, bConvertToCelsius. This is a Boolean variable, which means that it can have a value or either True or False.

Example 4.

Function CelsiusOrFahrenheit(dTemperature As Double, _
    bConvertToCelsius As Boolean) As Double
  If bConvertToCelsius Then     'dTemperature is in Fahrenheit,
                                'so convert to Celsius
        CelsiusOrFahrenheit = (dTemperature - 32) * (5 / 9)
    Else         'dTemperature is in Celsius, convert to Fahrenheit
        CelsiusOrFahrenheit = dTemperature * (9 / 5) + 32
    End If
End Function

Subroutines can also take parameters. Note, however, that a subroutine which requires parameters cannot be called directly from Excel’s Tools, Macros menu. It must be called from another subroutine (or function).

Using Your Functions in the Spreadsheet
The coolest thing about writing your own functions is that you can actually use them directly in your spreadsheet. All of the examples above are contained in the Excel VBA Article 2 Example Solution.xls (link above). If you haven’t already been adding the examples to your own workbook, download the Article 2 Example Solution workbook.

In order to use your function in the spreadsheet, you only need to make one minor change. In the VBA Editor, add the word "Public" before the word "Function". This simply makes the function accessible from outside the VBA module in which it resides.

Next go to the spreadsheet, enter a number (temperature) in one cell, then click in an empty cell. Select Insert, Function from the main menu. The last item in the Function Category list on the left is called User Defined. Click on this and you will see your user-defined functions listed magically on the right. Double-click on Celsius. You will then be presented with a box in which you are prompted for the parameter which the function requires, dFahrenheit. Click on the cell in which you typed a temperature, then click OK. Your temperature has been converted from Fahrenheit to Celsius!

I don’t know about you, but I was pretty excited when I learned I could write my own functions, then use them in my spreadsheet. Then again, maybe you’re not as big a geek as I.

Using Excel Functions in Your Code
Just as you can use your own functions in a spreadsheet, you can also use Excel functions in your code. This is done using the WorksheetFunction object (which is actually a property of the Application object). The WorksheetFunction object provides your VBA code with access to Excel worksheet functions. The example below (also in the Example Solution spreadsheet) calculates the average and standard deviation for each column of numbers on the second worksheet, and places the results below the numbers.

Example 5.

Sub UseExcelFunctions()

    Dim iColumnCount As Integer
    Dim lRowCount As Long
    Dim iCol As Integer
    Dim dAverage As Double
    Dim dStdDev As Double

    Worksheets(2).Activate
    ' Count number of columns and rows in use...
    iColumnCount = ActiveSheet.UsedRange.Columns.Count
    lRowCount = ActiveSheet.UsedRange.Rows.Count

    ' Cycle through each non-empty column...
    For iCol = 1 To iColumnCount
        ' Use the Average and StDev Excel functions to
        ' calculate values for each column...
        dAverage = WorksheetFunction.Average(Columns(iCol))
        dStdDev = WorksheetFunction.StDev(Columns(iCol))
        ' Place results below existing data...
        Cells(lRowCount + 2, iCol).Value = dAverage
        Cells(lRowCount + 3, iCol).Value = dStdDev
    Next iCol

End Sub

"Why Mark?" you ask. "Why write code to do this when I can simply put the actual formulas right in the spreadsheet?" While I admit I do not often use Excel functions in my VBA code, I can think of a couple of instances in which this might come in handy. One example might be if you had to calculate a number of statistics for multiple sets of data, each on a separate worksheet within your workbook, and list all of these statistics on a summary worksheet. This might be easier to do with code. Also, there may be times when you need to send a spreadsheet of calculated data to a client, and don’t want to have any formulas in the spreadsheet, only data. This is a circumstance I often encounter in my work.

An Exercise for You
If you’re game, here’s an exercise for you to try. I will present the solution in my next article. Your assignment, should you choose to accept it, is to write a function called "DayOfYear" which takes one parameter, a Date, and returns an integer which represents which day of the year that date represents. For example, if the date is Feb 1, 2004, the function should return 32. If Dec 31, 2004, it should return 366. Date is another VBA data type and represents both date and time. You will use various VBA date-related functions, such as Month, Day, and Year to write your function. Take a look at the VBA Excel help file for more information on the Date data type and these function.

The second part of the exercise is to write a second function called "ZodiacSign", which also takes a Date parameter, and returns a string representing the sign of the zodiac which corresponds to that date (Capricorn, Aquarius, etc). You will call your DayOfYear function from the ZodiacSign function. Add some dates in your spreadsheet and apply each of your functions to each date.

Here is some sample data you should try to match:

Date

DayOfYear

ZodiacSign

1/20/1999

20

Aquarius

12/31/2003

365

Capricorn

12/31/2004

366

Capricorn

6/16/2001

167

Gemini

7/10/1986

191

Cancer

3/19/1990

78

Pisces

9/6/1982

249

Virgo

One last hint… I’ve covered the "if" statement briefly, and the "else" clause that can go with it. For your functions, you will set up a series of conditions using "elseif", which allows your code to check if a second (or third, fourth…) condition is true when the previous condition is not. See example 6 below.

Example 6.

If iScore >= 90 Then
    sGrade = "A"
ElseIf iScore >= 80 Then
    sGrade = "B"
ElseIf iScore >= 70 Then
    sGrade = "C"
ElseIf iScore >= 60 Then
    sGrade = "D"
Else
    sGrade = "F"
End If

Good luck.


Need further help getting your Excel VBA code right? Join our free Excel VBA support group! See this link for details: http://groups.yahoo.com/group/ExcelVBA/ .

Click to rate this article.

 

Go up to the top of this page.
This site powered by the Logical Web Publisher™: Content management by Logical Expressions, Inc.