This lesson we will focus this lesson on Worksheet Functions in VBA for Excel. Excel has a number of functions that can be used in the Visual basic environment only, but it also has at it's disposal a long list of the standard Worksheet Functions. When these are combined with VBA for Excel it adds even more power and flexibility to the Visual Basic environment. Whenever you write a formula in Excel we must precede it with an = (equal sign). When we use the same formula or function in Excel we must precede it with the words "WorkheetFunction". The WorkheetFunction is a Object member of the Application Object and as such we do not need to use the full: Application.WorksheetFunction. You can see the full list of Worksheet Functions available to the us within VBE by looking under "List of Worksheet Functions Available to Visual Basic". It is a great idea to use Worksheet Functions within your code as it can often means doing away with unnecessary filling up of ranges with formulas. This in turn will mean a faster Workbook.
We will also look at making use of Excels built-in features and how to use them within VBA. I am of the very strong opinion that this is a MUST when using VBA for Excel, as combining the built-in features with VBA will result in being able to overcome almost any problems you are faced with. It will also keep us away from those Loops and use them only when really needed. As with Worksheet Functions we can use almost all of Excels built-in features and the only limit to their use is your own imagination. In particular, we will look at the Find, Autofilter, AdvancedFilter and SpecialCells (Go to Special in the Excel interface). There is no doubt at all in my mind that these built-in features will do for us in less than a second what could take 100 times as long by writing our own VBA procedure to do the same thing. Although this is patently clear to me I am always amazed at the amount of experienced VBA coders that insist on re-inventing the wheel.
Making life a bit easier
Before we look at some specific examples I should point out the guidance Excel will provide when you use any of the Worksheet Functions. As soon as you type: WorksheetFunction and then place the "." (period) after it, Excel will list all the Worksheet functions that are available in alphabetical order. Once you have picked the function you need you will type: ((an open parenthesis) and Excel will then show the expected syntax for that particular function. Sometimes this is enough to jolt the memory if the function is one that you have used before. At other times it won't be of much help at all. There are two ways I use to overcome this. For both examples I will use the VLOOKUP function
Switch back to the Excel interface (Alt+F11) then in any cell type: =vlookup
Then push Ctrl+Shift+A. This will give you the syntax for VLOOKUP.
Copy this from the Formula bar and switch back to the VBE (Alt+F11).
Paste this at the top of the Procedure as a comment.
Method 2
Switch back the Excel interface (Alt+F11) then in any cell type the formula as you would normally.
Copy this from the Formula bar and switch back to the VBE (Alt+F11).
Paste this at the top of the Procedure as a comment.
Then all you need to do is look at the comment and use this as a guide.
Specific examples
Sub SumColumn()
This makes use of the SUM function that is available to the Excel interface. As you are no doubt aware, the SUM function used in the Excel interface normally uses a range address as it's argument. When using Worksheet Functions with the Visual Basic environment we replace this with a Range Object. "Columns(1)" is a Range Object.
You will also notice that the SUM function can take up to 30 arguments, so we could supply up to another 29 Range Objects if needed. Which means we could also use:
Both of the above example will only SUM the values in the Range Object(s) stated of the ActiveSheet. We could do the same for three different sheets if needed.
Sub SumMoreThanOneColumnSeperateSheets()
Dim dResult As Double
Ok, so we know that we need to use a Range Object in any Worksheet Function that would normally use a range address. But a lot of Worksheet Functions will also accept values as their arguments. So if we had a number of numeric variables that we needed to know the sum total of, we could use:
In addition to using values or a Range Object we could also use the Selection Method. This of course would be a Range Object in this context providing the Selection was a range of cells and not some other type of Object. So the Procedure:
Is a perfectly valid Procedure.
COUNTIF
Sub UseCountIf()
Dim iResult As Integer
iResult = WorksheetFunction.CountIf(Range("A1:A10"), ">20")
MsgBox iResult
End Sub
VLOOKUP
Sub UseVlookUp()
Dim sResult As String
sResult = WorksheetFunction.VLookup("Dog", Sheet2.Range("A1:H500"), 3, False)
MsgBox sResult
End Sub
Be aware that if the text "Dog" does not exist in the first Column of Sheet2.Range("A1:H500") a Run-time error will be generated. This can be handled in a number of ways, but possibly the best would be to use a small If Statement.
There is really not much else that can be said about the use of Worksheet Formulas within the Visual Basic environment. I do though highly recommend using them in many situations as the speed at which they will supply an answer will always be far quicker than any VBA code written to do the same.
Excels Built-in Features
This would no doubt be one of Excel most under-utilised areas. It seems that when Excel users learn VBA they tend to forget the built-in features that are available to us in the Excel interface are still available in the Visual Basic environment. The examples I will show you can be applied to a lot of different situations and it will nearly always be worth sitting back and trying to think where they can be used. The easiest way to get the foundations for your code that makes use of one of Excels built-in features is without doubt by using the Macro Recorder, but it should only be a starting point not the code itself.
Find
When using the Find in VBA it is a good idea to parse the result to a properly declared variable. The reason for this is so that we can check the result to see if the value we are looking for has been found. To do this we must also use the: On Error Resume Next Statement so that our Procedure does not produce a Run-time error. The examples below are just some of the ways I have used this feature in my VBA code of past. For all examples I will use the ActiveSheet, but the examples can (and often are) used on another sheet.
Sub FindRow()
Dim lFound As Long .
On Error Resume Next
lFound = Cells.Find(What:="100", After:=Range("A1"), _
LookIn:=xlValues,LookAt:=xlWhole, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False).Row
If lFound = 0 Then
MsgBox "Cannot find 100"
Else
MsgBox "100 is on row " & lFound
End If
On Error GoTo 0
End Sub
Dim iCount As Integer
Dim rFound As Range
On Error Resume Next
Set rFound = Range("A1")
Do the procedure directly above (BoldAllDogs) using a standard Loop and you will have time to make and drink a coffee! Yet I see people doing similar time and time again.
I will also suggest strongly that you read the help file on the Find Method as it describes each argument in good detail. If you do not read it you will at least need to know this:
Remarks
The settings for LookIn, LookAt, SearchOrder, and MatchByte are saved each time you use this method. If you don’t specify values for these arguments the next time you call the method, the saved values are used. Setting these arguments changes the settings in the Find dialog box, and changing the settings in the Find dialog box changes the saved values that are used if you omit the arguments. To avoid problems, set these arguments explicitly each time you use this method.
AutoFilter and SpecialCells
We can check to see if the AutoFilters are on by using the AutoFilterMode Property. It will return True if they are on and False if they are off.
Sub AreFiltersOn()
If ActiveSheet.AutoFilterMode = False Then
MsgBox "Filters are off"
Else
MsgBox "Filters are on"
End If
End Sub
Sub TurnFiltersOff()
FilterMode
We can also check to see whether a Worksheet is in FilterMode, this will return True if the Worksheet currently has hidden rows as result of the AutoFilter. This is not the same as the AutoFilterMode Property, as FilterMode would return False if the sheet had AutoFilters turned on but not in use (not filtered down), while the AutoFilterMode would return True. This Property is Read Only so we cannot change it to False if True or True if False.
The other Method that we can use is the ShowAllData. This will set the AutoFilters back to "(All)" if they are currently in use (FilterMode = True). Be careful though as a Run-time error is generated if we use this method on a sheet that is not currently filtered (FilterMode = False). To avoid this we use the FilterMode Property to check first.
Ok, so that is all the means and ways to find out the current status of a sheet with regards to AutoFilters. What we can do now is move on to actually using the AutoFilter Method in some VBA code.
Let's say we wanted to copy all the rows of a sheet that have the word "dog" in Column C and place them on another Sheet. This is where the AutoFilter Property can help us.
Sub ConditionalCopy()
With ActiveSheet
If WorksheetFunction.CountIf(.Columns(3), "dog") <> 0 Then
.AutoFilterMode = False
.Range("A1:H1").AutoFilter
.Range("A1:H1").AutoFilter Field:=3, Criteria1:="Dog"
.UsedRange.SpecialCells(xlCellTypeVisible).Copy _
Destination:=Sheet2.Range("A1")
.AutoFilterMode = False
Application.CutCopyMode = False
End If
End With
End Sub
Let's step through this and see what we did:
If WorksheetFunction.CountIf(.Columns(3), "dog") <> 0 Then So the first thing we do is check to see whether the word "dog" is in Column C.
.AutoFilterMode = False We then set the .AutoFilterMode Property to False. We do not need to check if the AutoFilters are on because if they aren't nothing will happen. If they are on and/or in use they will be turned off.
.Range("A1:H1").AutoFilter We then apply the AutoFilters to the range we are interested in.
.Range("A1:H1").AutoFilter Field:=3, Criteria1:="Dog" We then set the Criteria Property to "Dog".
Field3 is Column C and is relavent to the range we filtered. .UsedRange.SpecialCells(xlCellTypeVisible).Copy Destination:=Sheet2.Range("A1") Here we have used the UsedRange Property and the SpecialCells Method to copy only the cells that are visible after applying the AutoFilter. We then paste them to Sheet2.Range("A1).
.AutoFilterMode = False
Application.CutCopyMode = False We then turn the filters off and clear the Clipboard.
Using this method we can easily make use of Excels Autofilter to get what we are after. What we need to do now is look at the SpecialCells Method. As I have already mentioned, this Method is the same as using Edit>Go to-Special. Recording a Macro doing just this is the best way to get the code you are wanting. Once you have done this a few times you will be able to skip the Recording bit. The text below is from the Excel help on SpecialCells and is worth taking 5 minutes to read:
SpecialCells Method
Returns a Range object that represents all the cells that match the specified type and value.
Syntax
expression.SpecialCells(Type, Value)
expression Required. An expression that returns a Range object.
Type Required Long. The cells to include. Can be one of the following XlCellType constants.
Constant | 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 |
xlCellTypeSameValidation | Cells having the same validation criteria |
xlCellTypeVisible | All visible cells |
Value Optional Variant. If Type is either xlCellTypeConstants or xlCellTypeFormulas, this argument is used to determine which types of cells to include in the result. These values can be added together to return more than one type. The default is to select all constants or formulas, no matter what the type. Can be one of the following XlSpecialCellsValue constants: xlErrors, xlLogical, xlNumbers, or xlTextValues.
AdvancedFilter
The last feature we will look at is the AdvancedFilter. This is feature is great for creating a list of unique items from a list. It can of course do a lot more than just this, but in the interest of keeping things simple I will only show how it can be used to create a unique list. Should you wish to go into this any deeper then I suggest Recording a Macro using this feature in the Interface and studying the code. If you have any problems or questions at all let me know and I will endeavour to help you. As with the SpecialCells Method it would pay to read the Excel help on AdvancedFilter
AdvancedFilter Method
Filters or copies data from a list based on a criteria range. If the initial selection is a single cell, that cell's current region is used.
Syntax
expression.AdvancedFilter(Action, CriteriaRange, CopyToRange, Unique)
expression Required. An expression that returns a Range object.
Action Required Long. The filter operation. Can be one of the following XlFilterAction constants: xlFilterInPlace or xlFilterCopy.
CriteriaRange Optional Variant. The criteria range. If this argument is omitted, there are no criteria.
CopyToRange Optional Variant. The destination range for the copied rows if Action is xlFilterCopy. Otherwise, this argument is ignored.
Unique Optional Variant. True to filter unique records only. False to filter all records that meet the criteria. The default value is False.
END OF EXCEL HELP
Let's look at an example of how we would copy a list that contained non unique items and paste it as list of unique items. Assume our list is in Column A.
This code would copy the list from Column A of the ActiveSheet to Column A of Sheet2.
Sheet2.Columns(1).Clear This is needed as the AdvancedFilter can generate a Run-time error if the CopyToRange contains data.
On Error Resume Next Will prevent any Run-time error should our list not contain enough data, ie less than 2 items.
.Range("A1", .Range("A65536").End(xlUp)) is used to define the range in Column A, starting from cell A1 down to the very last cell in Column A that contains an entry.
This simple bit of code would give us a unique list of items on a seperate Worksheet. You may not be aware that it is not possible to copy a unique list to another sheet via the AdvancedFilter in the Excel interface. I tell you this so when/if you Record a Macro, don't try and copy to another sheet. Just copy to any old range and change the code afterwards.
Summary
So by using the above examples as a starting point it is more often than not possible to do a task in VBA that will run very quick and clean. The only limit to the built-in features of Excel is usually your own imagination. I have yet to see any code that can operate as quickly or efficiently as one of Excels built-in features. They also give you the advantage of being able to Record the code needed to use the features. But remember, you should modify the code to become efficient! You will find that once you have been able to use one of Excels built-in features to do what you want, you will start thinking of other areas it can be adopted. I quite often will take an hour or more to rack my brains trying to come up with a efficient piece of code that fully utilises one of Excels built-in features. Once I have the idea it usually only takes a few minutes to put in place.
Click here to visit our Free 24/7 Excel/VBA Help Forum where there are thousands of posts you can get information from, or you can join the Forum and post your own questions.
See also: Index to Excel VBA Code and Index to Excel Freebies and Lesson 1 - Excel Fundamentals and Index to how to… providing a range of solutions and Index to new resources and reference sheets