Conditional Formatting Validation Macro

In Conditional Data Validation, I described how to set up conditional formatting to alert you when your list validation was no longer valid. I also noticed that this is covered in Professional Excel Development (and much better, I might add) so if you haven’t ordered your copy yet, I suggest you do.

I recently had cause to use this technique, but my lists were all over the place. I couldn’t set up the CF on one cell, then copy it to the others because the relationship between the cells wasn’t the same as the relationship between the lists. I created this macro to loop through all the cells on the sheet and if there’s list validation, it adds conditional formatting.

Sub FormatValidation()
     
    Dim rCell As Range
    Dim lValType As Long
    Dim CllFc As FormatCondition
   
    For Each rCell In Sheet1.UsedRange.Cells
   
        ‘set valtype to a number that is not valid for the enumeration
       lValType = -1
       
        ‘Attempt to read the cell’s validation type
       On Error Resume Next
            lValType = rCell.Validation.Type
        On Error GoTo 0
       
        ‘If the cell had validation and the type was ‘list’
       If lValType = xlValidateList Then
           
            ‘Delete existing conditional formatting
           On Error Resume Next
                rCell.FormatConditions.Delete
            On Error GoTo 0
           
            ‘Create format condition with a formula that looks like:
           ‘=ISERROR(MATCH(cell, validationlistrange, FALSE))
           With rCell.Validation
                Set CllFc = rCell.FormatConditions.Add(Type:=xlExpression, _
                    Formula1:=“=ISERROR(MATCH(“ & rCell.Address & “,” & _
                        Right(.Formula1, Len(.Formula1) – 1) & “,FALSE))”)
            End With
           
            CllFc.Interior.Color = vbRed
        End If
    Next rCell
           
End Sub

Inserting Events Template

When I want to write event code in a Workbook, I Alt-F11 to open the VBE, Cntl-R to open the Project Explorer, arrow to the class module in which the code will go, and… Then, as far as I can tell, I have to use my mouse and the dropdowns at the top of the module to insert the Sub and End Sub lines.

dropdowns at the top of the sheet module showing Worksheet and Change

I could just type them in, but I would to get the arguments just right or the VBE wouldn’t like it. It would be nice if the Insert > Procedure menu item had a provision for event code.

Insert procedure dialog box

The best solution I could muster is to create text files that contain the code I want. I have two text files in my Office folder (Program FilesMicrosoft Office 2000Office is the default folder for Insert > File) called SheetEvents.txt and WorkbookEvents.txt.

Insert file menu item

SheetEvents contains the Worksheet_Change and Workbook_SelectionChange events. WorkbookEvents contains Open, BeforeClose and BeforeSave. Those are the events I use most often. I thought about making a separate file for each event, but I decided to just put the most common ones in a file and delete anything I don’t need. Creating the text files was a simple copy and paste operation into Notepad after I used the dropdowns to get the events I wanted.

Playing WAV Files

Frank asks:

Dick, would it be a lot of trouble to make the American National Anthem play when displaying the flag?

First, go to the Embassy of the United States of America in Stockholm, Sweden’s website and download their wav file of the national anthem.

Next, go to The Spreadsheet Page and borrow John’s code for playing a wav file.

With a little modification, the code looks like this:

Private Declare Function PlaySound Lib “winmm.dll” _
  Alias “PlaySoundA” (ByVal lpszName As String, _
  ByVal hModule As Long, ByVal dwFlags As Long) As Long
 
Const SND_SYNC = &H0
Const SND_ASYNC = &H1
Const SND_FILENAME = &H20000
 
Sub PlayWAV(ByVal sWav As String)
    Call PlaySound(sWav, 0&, SND_ASYNC Or SND_FILENAME)
End Sub

and add this to the fly the flag code

    sWav = ThisWorkbook.Path & “anthem.wav”
    PlayWAV sWav

Don’t forget to declare sWav in your code. Or you can just download the file

Download FlyFlag.zip

A Touch of Grey

Yesterday was my birthday. No complicated birthday formulas this year, just a binary cake.

Birthday cake with eight candles with the third and sixth lit

I would apologize for the poor MS Paint job on that clip art, but if you’ve been reading this blog for any length of time, you know I stink at graphics.

I started browsing around for birthday cake clipart in Excel and came upon the Special Occasions section. It has balloons on the graphic, which is promising. I get in there (Insert > Picture > ClipArt) and see this:

insert clipart dialog box

What is happening in the second picture? Is that a guy bowling and using buildings for pins? Which special occasion does that represent, the rapture?

In picture 6, there’s a wizard looking through a telescope. I can only assumes you would use that when something good happens in your Dungeons and Dragons game. I may be nerdy enough to have a binary birthday cake, but I’m not so nerdy that I know enough D&D lingo to even make a decent joke.

Picture 7 depicts the “special occasion” when Egyptian slaves finished the pyramids. I don’t recall them wearing business suits, but to be fair, I wasn’t there.

Download PivotTable Parameters

There were a lot of comments that people couldn’t get parameters into their pivot tables. I didn’t save the workbook with which I made that post, but I was able to recreate it. You can download it:

Download PivotTableParameters.zip

I didn’t have any trouble recreating it, other than changing the Sheet2 reference in the code to Sheet1. I’m sure I’m just blowing by a step and not explaining it. Hopefully looking at the workbook will shed some light on why others couldn’t get it to work.

Note that the external data that my pivot table points to will not likely be in the same path on your machine. From the Immediate Window, my stuff looks like this:

?sheet1.PivotTables(1).pivotcache.connection
ODBC;DSN=MS Access Database;DBQ=C:Program FilesMicrosoft Office 2000OfficeSamplesNorthwind.mdb;DefaultDir=C:Program FilesMicrosoft Office 2000OfficeSamples;DriverId=25;FIL=MS Access;MaxBufferSize=2048;PageTimeout=5;

?sheet1.PivotTables(1).pivotcache.commandtext
SELECT Invoices.PostalCode, Invoices.ExtendedPrice
FROM ‘C:Program FilesMicrosoft Office 2000OfficeSamplesNorthwind’.Invoices Invoices
WHERE (Invoices.PostalCode=?)

You’ll have to change those paths to get it to work on your machine. You may, however, not need it to work, but just seeing the differences between this workbook and yours may be all you need.

Finding Modules and Procedures

Created by Stephen Bullen
The problem – you want to programatically obtain the name of the
VBComponent that contains a specified procedure. Stephen’s solution
was to look for unique strings, since the VBIDE object model does
not provide functionality for doing this directly.

Sub TestIt()
 
    MsgBox fnThisVBComponent(ThisWorkbook, “This Unique String”).Name & “, “ & _
        fnThisProcedureName(ThisWorkbook, “Another Unique String”)
 
End Sub
 
Function fnThisVBComponent(oBk As Workbook, sUniqueString As String) As VBComponent
 
    Dim oVBC As VBComponent
   
    ‘Loop through the VBComponents in the given workbook’s VBProject
   For Each oVBC In oBk.VBProject.VBComponents
   
        ‘Using it’s code module
       With oVBC.CodeModule
   
            ‘See if we can find the unique string
           If .Find(sUniqueString, 1, 1, .CountOfLines, 1000, True, _
            True, False) Then
   
                ‘Found it, so return the VBComponent where it was found
               Set fnThisVBComponent = oVBC
                Exit For
            End If
        End With
    Next
 
End Function
 
Function fnThisProcedureName(oBk As Workbook, sUniqueString As String) As String
 
    Dim oVBC As VBComponent
    Dim lStart As Long, sProcName As String, vaProcs As Variant, vProcType As Variant
   
    ‘Specify the row number of where to start the find.  This is set by
   ‘the Find method to give the (starting) line number where the text
   ‘was found. lStart = 1
   
    ‘Loop through the VBComponents in the given workbook’s VBProject
   For Each oVBC In oBk.VBProject.VBComponents
   
        ‘Using it’s code module
       With oVBC.CodeModule
   
            ‘See if we can find the unique string
           If .Find(sUniqueString, lStart, 1, .CountOfLines, 1000, True, _
            True, False) Then
   
                ‘We found it, so make an array of the available procedure
               ‘types to check for
   
                vaProcs = Array(vbext_pk_Proc, vbext_pk_Get, vbext_pk_Let, _
                vbext_pk_Set)
   
                ‘Loop throguh the procedure types
               For Each vProcType In vaProcs
   
                    ‘Get the name of the procedure containing the line we
                   ‘found above
                   sProcName = .ProcOfLine(lStart, CLng(vProcType))
   
                    ‘Did we get a procedure name?
                   If sProcName <> “” Then
   
                        ‘We did, so return it
                       fnThisProcedureName = sProcName
                        Exit For
                    End If
                Next
   
                Exit For
            End If
        End With
    Next
 
End Function

Be sure to set a reference to the Microsoft Visual Basic for Application Extensibility Library.