Custom Help for data validation help button

Keebellah

Board Regular
Joined
Feb 4, 2014
Messages
115
Hi guys,
I wonder if somebody could help me with this.
1662239913002.png


Using data validadtion I would like to have the Help button open a message bpox of whatever code I would attach to it instead of going to the Microsoft page.
Is that even possible?
Thanks in advance,
I'm using Excel 2019
 

Excel Facts

What is the shortcut key for Format Selection?
Ctrl+1 (the number one) will open the Format dialog for whatever is selected.
You can't open a new message box with that button.

You can, however, customize the error message box to show whatever message you desire.
You can also add an input message when the cell is selected.
 

Attachments

  • imagen_2022-09-03_180610.png
    imagen_2022-09-03_180610.png
    100.2 KB · Views: 10
  • imagen_2022-09-03_180625.png
    imagen_2022-09-03_180625.png
    122.8 KB · Views: 10
  • imagen_2022-09-03_180722.png
    imagen_2022-09-03_180722.png
    20.2 KB · Views: 11
  • imagen_2022-09-03_180804.png
    imagen_2022-09-03_180804.png
    2.5 KB · Views: 10
Upvote 0
You can't open a new message box with that button.

You can, however, customize the error message box to show whatever message you desire.
You can also add an input message when the cell is selected.
That I know, thank you, it’s really the help button, I have routines that display a pdf file in a userform as help and other similar, but I was wondering if you could trigger a cudtumized if to trigger something similar like invoke a userform with just extra information or a pdf file or whatever.
Thanks for thinking with me
 
Upvote 0
I think what you want can be achieved using the windows api but it is not going to be easy. Which versions of excel and OS are you using ?
 
Upvote 0
See if this works for you :
DataVal_HelpButton_Hook.xlsm




1- API code in a Standard Module:
VBA Code:
Option Explicit

#If VBA7 Then
    #If Win64 Then
        Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongPtrA" (ByVal hwnd As LongLong, ByVal nIndex As Long) As LongLong
        Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongLong, ByVal nIndex As Long, ByVal dwNewLong As LongLong) As LongLong
    #Else
        Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
        Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    #End If
    Private Declare PtrSafe Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As LongPtr, ByVal hwnd As LongPtr, ByVal Msg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
    Private Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
    Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
    Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
    Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" (ByVal pvInstance As LongPtr, ByVal oVft As LongPtr, ByVal cc As Long, ByVal vtReturn As Integer, ByVal cActuals As Long, ByVal prgvt As Long, ByVal prgpvarg As LongPtr, ByVal pvargResult As LongPtr) As Long
    Private Declare PtrSafe Function EnableWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal fEnable As Long) As Long
    Private Declare PtrSafe Function GetActiveWindow Lib "user32" () As LongPtr
    
    Private lPrevDVErrorBoxProc As LongPtr
#Else
    Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
    Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
    Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
    Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
    Private Declare Function DispCallFunc Lib "OleAut32.dll" (ByVal pvInstance As Long, ByVal oVft As Long, ByVal cc As Long, ByVal vtReturn As Integer, ByVal cActuals As Long, ByVal prgvt As Long, ByVal prgpvarg As Long, ByVal pvargResult As Long) As Long
    Private Declare Function EnableWindow Lib "user32" (ByVal hwnd As Long, ByVal fEnable As Long) As Long
    Private Declare Function GetActiveWindow Lib "user32" () As Long
    
    Private lPrevDVErrorBoxProc As Long
#End If

Private bStateOk As Boolean, bDVErrShowing As Boolean
    
    
    

'__________________________________________ PUBLIC CALLBACK ROUTINES _______________________________________________

Public Sub Macro1()
    ' Callback routines are unforgiving - No unhandled errors are allowed here.
    ' Propper error handling is necessary to avoid potential crashings.
    With UserForm1
        .Caption = "Custom Data Validation Help... Cell: " & ActiveCell.Address
        .Label1.Caption = "UserForm called from DV [Help] Button."
        .Show
    End With
End Sub

Public Sub Macro2()
    ' Callback routines are unforgiving - No unhandled errors are allowed here.
    ' Propper error handling is necessary to avoid potential crashings.
    Shell "calc.exe"
End Sub




'__________________________________________ PUBLIC ROUTINES _______________________________________________

#If Win64 Then
   Public Sub Customize_DataValidation_Help_Button( _
        ByVal ValidationCell As Range, _
        ByVal lpMacro As LongLong _
    )
#Else
  Public Sub Customize_DataValidation_Help_Button( _
        ByVal ValidationCell As Range, _
        ByVal lpMacro As Long _
    )
#End If

    If ValidationCell.Count = 1 Then
        If IsShowValidationError(ValidationCell) Then
            ValidationCell.ID = lpMacro
            If ValidationCell.Address = ActiveCell.Address Then
                bDVErrShowing = False
                Call StartDelayedHook(0)
            End If
        End If
    End If
End Sub

Public Sub SetWatcher(Optional ByVal bHook As Boolean = True)
    If bHook Then
        bStateOk = True
        Call SetTimer(Application.hwnd, 0, 0, AddressOf TimerProc)
    Else
        Call KillTimer(Application.hwnd, 0)
        Debug.Print "unhooked."
    End If
End Sub

Public Function Is_DataValidation_Help_Button_Customized(ByVal Cell As Range) As Boolean
    If Cell.Count > 1 Then Call SetWatcher(False): Exit Function
    Is_DataValidation_Help_Button_Customized = Len(Cell.ID)
End Function



'__________________________________________ PRIVATE ROUTINES _______________________________________________

Private Sub StartDelayedHook(ByVal lMsecDelay As Long)
    Call KillTimer(Application.hwnd, 0)
    Call SetTimer(Application.hwnd, 0, lMsecDelay, AddressOf DelayedSetHook)
End Sub

Private Sub DelayedSetHook()
    If GetActiveWindow = Application.hwnd Then
        Call KillTimer(Application.hwnd, 0)
        Call SetWatcher
    End If
End Sub

Private Sub TimerProc()
    Dim sClassName As String * 256, lRet As Long
    lRet = GetClassName(GetActiveWindow, sClassName, 256)
    If Left$(sClassName, lRet) = "#32770" And GetUserData(GetActiveWindow) = 0 Then
        If lPrevDVErrorBoxProc = 0 Then
            If bDVErrShowing = False Then
                bDVErrShowing = True
                Call Subclass(GetActiveWindow)
            End If
        End If
    Else
        bDVErrShowing = False
    End If
    If bStateOk = False Then
        Debug.Print "loss of state !"
        Call Subclass(GetActiveWindow, False)
        Call KillTimer(Application.hwnd, 0)
    End If
End Sub


#If Win64 Then
    Private Sub Subclass(ByVal hwnd As LongLong, Optional ByVal bSubclass As Boolean = True)
#Else
    Private Sub Subclass(ByVal hwnd As Long, Optional ByVal bSubclass As Boolean = True)
#End If
    Const GWL_WNDPROC = -4
    If bSubclass Then
        lPrevDVErrorBoxProc = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf SubclassProc)
    Else
        Call SetWindowLong(hwnd, GWL_WNDPROC, lPrevDVErrorBoxProc)
        lPrevDVErrorBoxProc = 0
        Debug.Print "unsubclassed."
    End If
End Sub

#If Win64 Then
    Private Function SubclassProc( _
        ByVal hwnd As LongLong, _
        ByVal Msg As Long, _
        ByVal wParam As LongLong, _
        ByVal lParam As LongLong _
    ) As LongLong
#Else
    Private Function SubclassProc( _
        ByVal hwnd As Long, _
        ByVal Msg As Long, _
        ByVal wParam As Long, _
        ByVal lParam As Long _
    ) As Long
#End If

    Const GWL_WNDPROC = -4
    Const WM_COMMAND = &H111
    Const WM_DESTROY = &H2
    Const BN_CLICKED = &H0
    Const CC_STDCALL = 4
    
    Dim vFuncRet As Variant
'
    Select Case Msg
        Case WM_COMMAND
            If hiword(CLng(wParam)) = BN_CLICKED Then
                If loword(CLng(wParam)) = &H9& Then  'DV Help button clicked.
                    #If Win64 Then
                        Dim FuncAddress As LongLong
                        FuncAddress = VBA.CLngLng(ActiveCell.ID)
                    #Else
                        Dim FuncAddress As Long
                        FuncAddress = VBA.CLng(ActiveCell.ID)
                    #End If
                    If FuncAddress Then
                        Call EnableWindow(lParam, False)
                        'Call our alternative Help macro.
                        Call DispCallFunc(0, FuncAddress, CC_STDCALL, vbEmpty, 0, 0, 0, VarPtr(vFuncRet))
                        Call EnableWindow(lParam, True)
                    End If
                    Call SetWatcher(False)
                    Call SetTimer(Application.hwnd, 0, 0, AddressOf ReHook)
                    'Abort default action of help button.
                    Exit Function
                End If
            End If
        Case WM_DESTROY
            Call Subclass(hwnd, False)
            Call KillTimer(Application.hwnd, 0)
    End Select
    
    SubclassProc = CallWindowProc(lPrevDVErrorBoxProc, hwnd, Msg, wParam, ByVal lParam)
End Function

Private Sub ReHook()
    Call KillTimer(Application.hwnd, 0)
    Call SetWatcher
End Sub

Private Function IsShowValidationError(ByVal Cell As Range) As Boolean
    On Error Resume Next
    IsShowValidationError = Cell.Validation.ShowError
End Function

#If Win64 Then
    Private Function GetUserData(ByVal hwnd As LongLong) As LongLong
#Else
    Private Function GetUserData(ByVal hwnd As Long) As Long
#End If
    Const GWL_USERDATA = (-21)
    GetUserData = GetWindowLong(hwnd, GWL_USERDATA)
End Function

Private Function hiword(DWord As Long) As Long
    hiword = (DWord And &HFFFF0000) \ &H10000
End Function

Private Function loword(DWord As Long) As Integer
    If DWord And &H8000& Then
        loword = DWord Or &HFFFF0000
    Else
        loword = DWord And &HFFFF&
    End If
End Function

Private Sub auto_close()
    Call SetWatcher(False)
    Call KillTimer(Application.hwnd, 0)
End Sub



2- Code Usage: (as per uploaded workbook example)

In the ThisWorkbook Module:
VBA Code:
Option Explicit

Private Sub Workbook_Open()
    Call Customize_DataValidation_Help_Button(Range("F3"), AddressOf Macro1)
    Call Customize_DataValidation_Help_Button(Range("F12"), AddressOf Macro2)
End Sub

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
    If Is_DataValidation_Help_Button_Customized(Target) Then
        Call SetWatcher(True)
    Else
        Call SetWatcher(False)
    End If
End Sub
 
Upvote 0
If you have Protected View Enable Editing on, you may get a runtime error upon opening the workbook from the above link.

I have slightly amended the code to prevent that error from happening Plus, I have also placed the Runtest and the callback macro routines (Macro1 and Macro2) in a separate bas module for esay use and for more clarity.

Here is the new updated workbook that you should be using:
DataVal_HelpButton_Hook.xlsm
 
Upvote 0
Solution
Thanks, will download it tonight when I’m back home and give it a run.
Thanks again got the effort and the sample file👍
 
Upvote 0
Jaafar, wokrs great, will have to dive in deeper to understand it but great sample and enough explanation.
Thank you tahnk you and thnak you
 
Upvote 0

Forum statistics

Threads
1,223,888
Messages
6,175,217
Members
452,619
Latest member
Shiv1198

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top