0
votes

I want to find a way to dynamically add hyperlinks to my Excel-Sheet and run macros depending on some cell contents. But neither the HYPERLINK-formula nor the regular hyperlink feature in Excel allow you to call macros directly from the worksheet. Looking for that problem online will always retrieve the option to use the Worksheet_FollowHyperlink event. But for my purpose this option is not suitable as you either have to write your macro to like "if target.range.address = A1 call macroA elseif target.cell = A2 call macro ...." etc... This solution is way too static in my opinion as you have to "hardwire" too much in your Worksheet_FollowHyperlink code. Furthermore you have to prepare the hyperlinks via VBA to change the address and subaddress to "" to avoid unwanted selection changes or error popups from excel (because some adress could not be found).

The =HYPERLINK()-formula looks way more interesting since you can dynamically create it wherever and whenever needed. It also works fine as a column-function inside a table which is what I actually want to do: Have a column filled with hyperlinks inside a table that will run macros with some given parameters depending on the other contents in each table data row. This would not work with regular hyperlinks at all as the user has to copy & paste them manually into every single row.

Sadly the =HYPERLINK()-formula also offers no option to run a macro directly with the given parameters (at least none that I could find). It will not even fire the Worksheet_FollowHyperlink event so it appears to be a dead end at this point. Interesting feature I found during my trial and error + internet research: =HYPERLINK("#TestMe", "Some text here...") will open the VBA-editor and jump directly to my TestMe() sub. Yet it will not be called!

What could be the solution to this problem?

  • Create Hyperlinks dynamically in a table data column
  • Call a macro depending on the data row contents
1
The title is a little misleading - it really should be "run macro with selectionchange event"Tim Williams

1 Answers

0
votes

I had the idea to use the Workbook_SheetSelectionChange event to monitor if a cell with a HYPERLINK-formula was selected and it turned out very well.

First revision of my code:

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
    Dim MacroName As String
    If Target.Cells.Count > 1 Then Exit Sub
    If Target.Formula Like "=HYPERLINK(LEFT(""|""*""|"",*),*)" Then
        MacroName = Split(Target.Formula, """|""")(1)
        MacroName = VBA.Trim(Replace(MacroName, "&", ""))
        MacroName = Sh.Evaluate(MacroName)
        
        Application.Run Macro
    End If

End Sub

It requires to have a cell with the following formula: =HYPERLINK(LEFT("|" & A1 & "|", 0), "Run Macro in A18") where cell A1 contains the name of some macro I want to run. The name of the macro could also be hardwired in the formula.

Note: the LEFT(..., 0) part is needed so the address of the hyperlink will appear empty to excel when clicking it. Otherwise it will bother you with an error popup for not finding the target.

Unfortunately the SelectionChange event also fires when selecting a cell with return-key, tab-key or arrow keys. To filter these out, you will need the following API-call:

Declare PtrSafe Function GetAsyncKeyState Lib "user32" (ByVal vkey As Integer) As Boolean

This function checks if a key is pressed at the moment it gets called. Source is this unresolved question: How to run code when clicking a cell?

The next evolution of the code above now looks like this:

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
    If GetAsyncKeyState(vbKeyTab) _
         Or GetAsyncKeyState(vbKeyReturn) _
         Or GetAsyncKeyState(vbKeyDown) _
         Or GetAsyncKeyState(vbKeyUp) _
         Or GetAsyncKeyState(vbKeyLeft) _
         Or GetAsyncKeyState(vbKeyRight) _
         Or Target.Cells.Count > 1 _
         Or VBA.TypeName(Sh) <> "Worksheet" _
    Then Exit Sub
    
    Dim Macro As String
    
    If Target.Formula Like "=HYPERLINK(LEFT(""|""*""|"",*),*)" Then
        Macro = Split(Target.Formula, """|""")(1)
        Macro = VBA.Trim(Replace(Macro, "&", ""))
        Macro = Sh.Evaluate(Macro)
        
        Application.Run Macro
    End If

End Sub

This now will filter out all selection changes done by key commands. Yet there is one more step to take as I had to notice there seems to be a flaw when changing a cell above or left of my hyperlink and hit return key or tab key. For some reason the GetAsyncKeyState will return false for both keys so my code would continue to run.

So for these situations I had to create a little dirty work around. You will need the Workbook_SheetChange event to set a switch which temporarily disables the Workbook_SheetSelectionChange event.

Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
    RecentSheetChange = True
    Application.OnTime VBA.DateAdd("s", 0.1, Now), "ResetRecentSheetChange"
End Sub

'Code inside a new module:

Option Explicit
Option Private Module

Declare PtrSafe Function GetAsyncKeyState Lib "user32" (ByVal vkey As Integer) As Boolean
Public RecentSheetChange As Boolean

Private Sub ResetRecentSheetChange()
    RecentSheetChange = False
End Sub

The final code in ThisWorkbook now looks like this:

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
    If GetAsyncKeyState(vbKeyTab) _
         Or GetAsyncKeyState(vbKeyReturn) _
         Or GetAsyncKeyState(vbKeyDown) _
         Or GetAsyncKeyState(vbKeyUp) _
         Or GetAsyncKeyState(vbKeyLeft) _
         Or GetAsyncKeyState(vbKeyRight) _
         Or Target.Cells.Count > 1 _
         Or VBA.TypeName(Sh) <> "Worksheet" _
         Or RecentSheetChange _
    Then Exit Sub
    
    Dim Macro As String
    
    If Target.Formula Like "=HYPERLINK(LEFT(""|""*""|"",*),*)" Then
        Macro = Split(Target.Formula, """|""")(1)
        Macro = VBA.Trim(Replace(Macro, "&", ""))
        Macro = Sh.Evaluate(Macro)
        
        Application.Run Macro
    End If

End Sub

Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
    RecentSheetChange = True
    Application.OnTime VBA.DateAdd("s", 0.1, Now), "ResetRecentSheetChange"
End Sub

Adding parameter features to the hyperlink is only a small step from here.

Your thoughts?