Detect the event ActiveWorkbook.Save from personal.xlsm in excel - vba

I have all macros stored in personal.xlsm and untill now I have used a standard filter to hide/show columns. The new feature I want to implement now is that each user can have their own filter if they would like to. So basically i look in a folder for a personal filter if it exist and if it does it use that filter instead of the standard one.
But my problem is that i want to load a personal filter on workbookOpen event and reset to standard filter on the beforeClose event. My question is if I can do this from personal.xlsm in a way? Or do I have to manually go through all 250 workbooks and add in thisworkbook module onOpen and beforeClose events to call my method createFilter and resetFilter?
Here is a link to personal.xlsm for those who are not familiar with that methodology

In personal.xlsm, create a class module named "Helper". Place the following code in it:
Option Explicit
Public WithEvents xlApp As Application
Private Sub Class_Initialize()
Set xlApp = Application
End Sub
Private Sub xlApp_WorkbookOpen(ByVal Wb As Workbook)
'Your code here
End Sub
Your code for loading a filter should go in the 'Your code here bit.
Add a standard code module and add this code:
Public MyHelper As Helper
Finally, in the Workbook_Open event of personal.xlsm, place this code:
Private Sub Workbook_Open()
Set MyHelper = New Helper
End Sub
Save personal.xlsm and restart Excel.

Here is the code in helper if someones needs it:
Private Sub xlApp_WorkbookOpen(ByVal Wb As Workbook)
Dim myPath As String
On error resume next
If InStr(UCase(ActiveWorkbook.Name), "PARTSLIST") And (InStr(UCase(ActiveWorkbook.Name), "FILTERPARTSLIST") = 0) Then
myPath = Left(ThisWorkbook.Path, InStrRev(ThisWorkbook.Path, "\") - 1)
If Dir(myPath & "\filterPartsList.xlsx") <> "" Then '
Call ColumnCreater.updateFilter
Else
Call ColumnCreater.filterCreationStandard
End If
End If
End Sub

Related

Document_Open() does not work in other files

I am writing a VBA project containing 1 module (m1) and 1 userform (uf).
In "ThisDocument" I am calling a public sub from "m1" that initializes a collection I then refer to in the userform. This works perfectly fine until I deploy this project to other files.
I save the file as a .dotm in the %Appdata%/Microsoft/word/startup folder so I can use the project in all of my word files. But as soon as I try to use my project in other files the userform opens itself as designed but the collection is empty.
What could be the problem here?
Manually calling the initialization method from the userform works fine.
'----------------------------------------------ThisDocument
Private Sub Document_Open()
initBetaCollection
End Sub
'----------------------------------------------m1
Option Explicit
Public beta As Collection
Sub initBetaCollection()
Set beta = New Collection
beta.Add Array("0041", "A"), Key:="0041"
'...
End Sub
'----------------------------------------------uf
Option Explicit
Private Sub txtSearch_Change()
Dim arr As Variant
Dim search As String
'Defining the textinput as "search"
search = txtSearch.Value
For Each arr In beta
If search <> "" Then 'No empty search field
If arr(1) Like "*" & search & "*" Then 'Match found
lbResults.AddItem arr(0)
End If
End If
Next
End Sub
I get the: Run Time Error '424' object required
The problem with using Document_Open in the ThisDocument class or a macro named AutoOpen in a regular module is that both execute specifically for this document (or documents created from the template), only.
In order to have an application-level event, that fires for all documents opened, it's necessary to work with application-level events.
For this, first a class module is required. In the class module, the following code is needed:
'Class module named: Class1
Public WithEvents app As Word.Application
Private Sub app_DocumentOpen(ByVal Doc As Document)
'MsgBox Doc.FullName & ": on Open"
'Code here that should fire when a document is opened
'If something needs to be done with this document, use
'the Doc parameter passed to the event, don't try to use ActiveDocument
End Sub
Then, in a regular module, an AutoExec macro can be used to initialize the class with event handling. (AutoExec: fires when Word starts and loads a template with a macro of this name.)
Option Explicit
Dim theApp As New Class1
Sub AutoExec()
'MsgBox "AutoExec"
Set theApp.app = Word.Application
End Sub
'Sub AutoOpen()
' MsgBox "Open in AutoOpen" - fires only for this document
'End Sub

VBA – Using One Click Event for Multiple Toggle Buttons

I have a whole bunch of toggle buttons on 1 sheet, and I want them to have identical _click events. Is there a way I can do this without writing a new _click function for every single button?
Thanks!
Another way would be to use a class module to control the events for your controls...
1) Insert a class module (VBE > Insert > Class Module).
2) In the Properties window under Name, enter clsToggleButtonGroup.
3) Copy and paste the following code into the code module for the class...
Option Explicit
Public WithEvents ToggleButton As MSForms.ToggleButton
Private Sub ToggleButton_Click()
MsgBox ToggleButton.Caption
'do stuff
'
'
End Sub
4) In a regular module (VBE > Insert > Module), copy and paste the following code...
Option Explicit
Public colToggleButtons As New Collection
Sub AddToggleButtonsToClass()
Dim OleObj As OLEObject
Dim cToggleButton As clsToggleButtonGroup
For Each OleObj In Worksheets("Sheet1").OLEObjects 'change the sheet name accordingly
If TypeName(OleObj.Object) = "ToggleButton" Then
Set cToggleButton = New clsToggleButtonGroup
Set cToggleButton.ToggleButton = OleObj.Object
colToggleButtons.Add cToggleButton
End If
Next OleObj
End Sub
5) Then, run AddToggleButtonsToClass.
Short answer: nope.
Longer answer: nope. But you don't have to copy/paste the whole entire logic for every handler. Just extract that logic into its own Private method, and invoke it from the handlers.
Private Sub RunToggleButtonLogic()
'do stuff
End Sub
Private Sub ToggleButton1_Click()
RunToggleButtonLogic
End Sub
Private Sub ToggleButton2_Click()
RunToggleButtonLogic
End Sub
Private Sub ToggleButton3_Click()
RunToggleButtonLogic
End Sub
Private Sub ToggleButton4_Click()
RunToggleButtonLogic
End Sub
Private Sub ToggleButton5_Click()
RunToggleButtonLogic
End Sub
...
The reason for this is that VBA is COM, and COM events work with interfaces (like Java), not delegates (like C#, or VB.NET) - so you can't "assign" a handler to an event, it simply doesn't work that way.

Workbook_BeforeSave with every active workbook ActiveWorkbook

As I very often have to problem, that the tick vanishes in the settings: Calculate before save. (I don't know the exact term as my office version is in German).
That's why I tried to use VBA to solve the problem. I used the following code in my Excel file:
Option Explicit
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, _
Cancel As Boolean)
If Application.CalculateBeforeSave = False Then
If MsgBox("Caution! Should >calculatebeforesave< be activated?", vbYesNo) = vbYes Then
Application.CalculateBeforeSave = True
Else
End If
Else
End If
End Sub
I put this into "Thisworkbook". But I would like this code to be ran in every workbook I work with (at least all these which allow for macros).
My suggestion was to write ActiveWorkbook_BeforeSave... instead of Workbook_BeforeSave and then put the code in a module in the PERSONAL Macro file. But this doesn't work.
I think you need to use the Excel Applications events rather than workbook events to achieve this, such as in this example
In your PERSONAL workbook right click and insert a a Class Module (Class 1)
Add something similar to below to Class 1:
Public WithEvents appevent As Application
Private Sub appevent_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
'Add what you would like to happen before a workbook closes
End Sub
Next open ThisWorksheet and add code along these lines (I think the PERSONAL workbook opens automatically when Excel starts):
Dim myobject As New Class1
Private Sub Workbook_Open()
Set myobject = Application
End Sub

Using Events with the Application Object in MS Word

Ms Word does not want to load my add-in. I want to call a userform on print event. Here is my code:
in module 1
Option Explicit
Private Sub App_DocumentBeforePrint(ByVal Doc As Document, Cancel As Boolean)
'Debug.Print Now & " " & "App_DocumentBeforePrint: " & Wb.FullName
Userform1.Show
End Sub
Sub InitializeApp()
Dim X As New EventClassModule
Set X.App = Word.Application
End Sub
in Document module
Private Sub Document_Open()
Call InitializeApp
End Sub
in EventClassModule
Public WithEvents App As Word.Application
in Userform1 Mode
Option Explicit
Private Sub UserForm1_Initialize()
End Sub
I used this 2 links to help me write this code
1) https://msdn.microsoft.com/en-us/library/bb221264%28v=office.12%29.aspx
2) https://msdn.microsoft.com/en-us/library/gg597509%28v=office.14%29.aspx
Can anyone tell why my code does not work?
As explained in the first link you show, the procedure App_DocumentBeforePrint needs to be in the CLASS module (EventClassModule, in your explanation), not in Module 1.
Other than that, it's not clear what you mean by "my add-in". Usually, I'd think of a template (or COM add-in) when this term is used that's being loaded as an add-in. I'm concerned whether Document_Open is actually being triggered to initialize your events. This event, in the ThisDocument module (in reality, it's a class) will only fire when the document containing this code is opened...
For anybody else who comes across this thread like I did, this is what worked for me:
in module 1
(your module shouldn't contain the event-based sub; also, X needs to be declared as a global variable rather than within the 'InitializeApp' sub)
Option Explicit
Dim X As New EventClassModule
Sub InitializeApp()
Set X.App = Word.Application
End Sub
in Document module
Private Sub Document_Open()
Call InitializeApp
End Sub
in EventClassModule
(your Class Module should contain the event-based sub)
Public WithEvents App As Word.Application
Private Sub App_DocumentBeforePrint(ByVal Doc As Document, Cancel As Boolean)
'''Your procedure here
End Sub

How to Call Subs from an Add-In Directly

So it's obviously easy to write code that will call a sub/function that is in an add-in library via VBA code, simply by doing
call myFunctionOrSub
However, is there a way to allow users to directly call public subs in an add-in? For example, when the user goes to Tools -> Macros and pulls up this screen:
I would like to add to the list of macros in that box all Subs which are included in add-ins that are linked to for the file. That is, I have a library (library.xlam) that is referenced by this current workbook. In this library.xlam file, I have Subs (such as copyToResults). I want copyToResults to appear as a runnable macro in this list. Is there a way to do that?
The only solution I could come up with was to create a Sub in my test file for each Sub in library.xlam. This Sub in the test file would do nothing by call library's Sub. However, this is terrible for the purpose of having external libraries and terrible for scalability, so we definitely don't want to go this route.
Make a form in your xlam with a list box.
Use the script from this post to populate your form. You will have to change some excel settings.
Get a list of the macros of a module in excel, and then call all those macros
Here is my code from my form:
Private Sub btnCancel_Click()
Unload Me
End Sub
Private Sub btnExecute_Click()
Application.Run "macros.xlam!" & lstMacros.Value
Unload Me
End Sub
Private Sub UserForm_Initialize()
Dim pj As VBProject
Dim vbcomp As VBComponent
Dim curMacro As String, newMacro As String
Dim x As String
Dim y As String
Dim macros As String
On Error Resume Next
curMacro = ""
Documents.Add
For Each pj In Application.VBE.VBProjects
For Each vbcomp In pj.VBComponents
If Not vbcomp Is Nothing Then
If Not vbcomp.CodeModule = "Utilities" Then
For i = 1 To vbcomp.CodeModule.CountOfLines
newMacro = vbcomp.CodeModule.ProcOfLine(Line:=i, _
prockind:=vbext_pk_Proc)
If curMacro <> newMacro Then
curMacro = newMacro
If curMacro <> "" And curMacro <> "app_NewDocument" Then
frmMacros.lstMacros.AddItem curMacro
End If
End If
Next
End If
End If
Next
Next
End Sub
In the end mine looked like this:
Macros Form