Trapping outline expand / collapse

Jon von der Heyden

MrExcel MVP, Moderator
Joined
Apr 6, 2004
Messages
10,907
Office Version
  1. 365
Platform
  1. Windows
I suspect I need a class module for this...

How do I trap the event when a user chooses to expand / collapse outlines (columns in this instance) on a worksheet?

Basicallly, when attempting to expand I want to prompt the user to enter a password and then unprotect the sheet. And if collapsing to prompt again and then protect the sheet. I have this bit covered, just not sure how to trap the event.

Thanks!
 

Excel Facts

How to show all formulas in Excel?
Press Ctrl+` to show all formulas. Press it again to toggle back to numbers. The grave accent is often under the tilde on US keyboards.
You could use the Calculate event for this.

If you place a dummy formula containg a volatile function like say (=Now()) somwhere at the bottom of the outlined columns , the expanding and collapsing will recalculate the formula and you can take advantage of the worksheet calculate event to protect/unprotect the sheet.

If you have more than one outline, the calculate event may fire more than once though but hopfully, that can be worked around.

Regards.
 
Upvote 0
Hi Jafaar

Thanks for the reply. I'm not sure I can see this working though. What would I use to stop the prompt and protection from running when the calculation is fired up through other events (i.e. changing an input cell that drives result in another formula)?
 
Upvote 0
Hi Jafaar

Thanks for the reply. I'm not sure I can see this working though. What would I use to stop the prompt and protection from running when the calculation is fired up through other events (i.e. changing an input cell that drives result in another formula)?

You are right Jon.- I forgot about that.
I am off at the moment but will get back to this later.

Regards.
 
Upvote 0
Can you post the exact first couple of words on the excel prompt dialog that comes up when the worksheet is protected and you click on the + button to expand the outlines ?

The reason i am asking this is because i am using a french version of excel and the workround i am thinking of doing via code will need the corresponding text in English in order to identify and trap the dialog as soon as is displayed.

Regards.
 
Upvote 0
Hi Jon,

Sorry for the delay.

here is an example workbook that will prompt the user to enter the password "Jon" to expand the outline in sheets(1).columns A:E and unprotect the worksheet.

Likewise, when the user collapses the outline, he will be prompted by a MsgBox informing them that the worksheet is now back to protected.

http://www.savefile.com/files/2114106

Place this in a Standard module :

Code:
Option Explicit
 
Private Declare Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" _
(ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long
 
Private Declare Function UnhookWindowsHookEx Lib "user32" _
(ByVal hHook As Long) As Long
 
Private Declare Function CallNextHookEx Lib "user32" _
(ByVal hHook As Long, _
ByVal ncode As Long, _
ByVal wParam As Long, _
lParam As Any) 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 GetWindowText Lib "user32" _
Alias "GetWindowTextA" _
(ByVal hwnd As Long, _
ByVal lpString As String, _
ByVal cch As Long) As Long
 
Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
Private Declare Function FindWindowEx _
Lib "user32" Alias "FindWindowExA" _
    (ByVal hWnd1 As Long, _
    ByVal hWnd2 As Long, _
    ByVal lpsz1 As String, _
    ByVal lpsz2 As String) As Long
 
Private Declare Function SendMessage Lib "user32" _
Alias "SendMessageA" _
(ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any) 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 Const WH_CBT As Long = 5
Private Const HCBT_ACTIVATE = 5
Private Const BM_CLICK = &HF5
Private lhHook As Long
Private bHookEnabled As Boolean
Private lTimerId As Long
Private oOutlinedColumns As Range
Private oRightColumnOfOutlinedRange As Range
'excel french version
'--------------------
Private Const FRENCH_EXCEL_PROTECTION_WARNING As String = _
"Vous ne pouvez pas exécuter cette commande sur une feuille"
'excel english version
'--------------------
Private Const ENGLISH_EXCEL_PROTECTION_WARNING As String = _
"You cannot use this command on a protected sheet"
Private sEXCEL_PROTECTION_WARNING As String
Private Const PASSWORD As String = "JON"
 
Sub CreateHook()
 
    Dim Country_Code As Long
 
    On Error Resume Next
    'install a cbt hook to monitor for wnds creation
    If Not bHookEnabled Then
        Country_Code = Application.International(xlCountryCode)
        If Country_Code = 1 Then
            sEXCEL_PROTECTION_WARNING = ENGLISH_EXCEL_PROTECTION_WARNING
        ElseIf Country_Code = 33 Then
            sEXCEL_PROTECTION_WARNING = FRENCH_EXCEL_PROTECTION_WARNING
        End If
        Set oOutlinedColumns = _
        Sheets(1).Columns("A:E") 'change this as required.
        With oOutlinedColumns
            Set oRightColumnOfOutlinedRange = _
            .Columns(.Columns.Count).Offset(, 1)
        End With
        ActiveSheet.Unprotect
        oRightColumnOfOutlinedRange.ShowDetail = False
        ActiveSheet.Protect
        lhHook = SetWindowsHookEx _
        (WH_CBT, AddressOf HookProc, 0, GetCurrentThreadId)
        bHookEnabled = True
    Else
        MsgBox "Already enabled.", vbCritical
    End If
 
End Sub
 
Sub DestroyHook()
 
    'important to unhook when done!
    UnhookWindowsHookEx lhHook
    bHookEnabled = False
 
End Sub
 
Private Sub ValidatePassword()
 
    Dim sPassword As String
 
    sPassword = InputBox _
    ("Enter password to exapand the columns.", "Password")
    If UCase(sPassword) = PASSWORD Then
        Call TimerCaller
    ElseIf sPassword = vbNullString Then
        MsgBox "You Cancelled.", vbInformation
    Else
        MsgBox "Wrong password.", vbCritical
    End If
 
End Sub
 
Private Sub TimerCaller()
 
    lTimerId = SetTimer(0, 0, 1, AddressOf TimerProc)
 
End Sub
 
Private Sub TimerProc()
 
    KillTimer 0, lTimerId
    ActiveSheet.Unprotect
    oRightColumnOfOutlinedRange.ShowDetail = True
 
End Sub
 
Private Function HookProc _
(ByVal idHook As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long
 
    Dim strBuffer As String
    Dim sBuffer2 As String
    Dim lRetVal As Long
    Dim lPromptTextHwnd As Long
    Dim lPromptButnHwnd As Long
 
    On Error Resume Next
    If oRightColumnOfOutlinedRange.ShowDetail = False _
    And ActiveSheet.ProtectContents = False Then
        ActiveSheet.Protect
        MsgBox "The sheet is now protected.", vbInformation
    End If
 
    'a wnd has been cretaed
    If idHook = HCBT_ACTIVATE Then 'HCBT_CREATEWND Then
        strBuffer = Space(256)
        lRetVal = GetClassName(wParam, strBuffer, 256)
        'is the wnd is the sheet tab window ?
        If Left(strBuffer, lRetVal) = "#32770" Then
            lPromptTextHwnd = _
            FindWindowEx(wParam, 0, "MSOUNISTAT", vbNullString)
            If lPromptTextHwnd <> 0 Then
                sBuffer2 = Space(256)
                GetWindowText lPromptTextHwnd, sBuffer2, 256
                If InStr(1, Left(sBuffer2, Len(sBuffer2) - 1), _
                    sEXCEL_PROTECTION_WARNING, vbTextCompare) Then
                    lPromptButnHwnd = _
                    FindWindowEx(wParam, 0, "BUTTON", vbNullString)
                    Call DestroyHook
                    HookProc = 1
                    SendMessage lPromptButnHwnd, BM_CLICK, 0, 0
                    Call ValidatePassword
                    Call CreateHook
                End If
            End If
        End If
    End If
    'Call next hook
    HookProc = CallNextHookEx _
    (lhHook, idHook, ByVal wParam, ByVal lParam)
 
End Function

Place this in the Workbook module

Code:
Option Explicit
 
Private Sub Workbook_Open()
 
    Call CreateHook
 
End Sub
 
Private Sub Workbook_BeforeClose(Cancel As Boolean)
 
    Call DestroyHook
 
End Sub

Note that this code uses a CBT hook and therefore will have an impact on performance.Also, editing the code carries a potential risk for crashing the application so use with caution. Having said that, I don't think there is another way of achieving what you want as there is no such native event for expading/collapsing worksheet outlines.

Any probs, please let us know.

Regards.
 
Upvote 0
Hello Jaafar

Thank you so much! I have noticed that it crashes when I try create a new workbook whilst this one is open. Or actually it doesn't seem to kill the application, but the msgbox 'The sheet is now protected' seems to reappear and stops me from being able to do anything. Other than that it seems to behave as expected.

But... I think I need to strongly rethink my need for this. I won't pretend to understand it and it's so far outside of my comfort zone. :eek:

I would love to learn API but me thinks it's a subject that takes many years to master... All the more reason to start learning now. :)

Thanks again, I really appreciate your help.
 
Upvote 0
I have noticed that it crashes when I try create a new workbook whilst this one is open.
It just goes to show how unpredictable subclassing and/or Hooking can be in office applications which is unfortunate given the potential these techniques have for enhancing Excel capabilities.

But... I think I need to strongly rethink my need for this.
Indeed, sometimes, the best solution lies in rethinking the application's logic/design.

Regards.
 
Upvote 0

Forum statistics

Threads
1,215,559
Messages
6,125,517
Members
449,236
Latest member
Afua

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