When new Reference is added, excel stops firing events - vba

I have this code in ThisWorkbook:
Public WithEvents App As Application
Private Sub Workbook_Open()
Application.EnableEvents = True
' it is a simple control RefEdit, but it could be anything:
ThisWorkbook.VBProject.References.AddFromGuid{00024517-0000-0000-C000-000000000046},0,0
Set App = Application
End Sub
Private Sub App_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
Debug.Print "Selection changed"
End Sub
This code adds new Reference on Workbook open event. When the line which adds reference from GUID is executed, excel stops firing SheetSelectionChange events. As well as other events. If I comment that line, excel fires all events successfully. Could you tell me what I am doing wrong? Is it good place to invoke functions to add new references in a project?

Obviously Events and adding a new references do not love each other - reference.
This is the way around it, proposed there:
Private Sub Workbook_Open()
' 'Must have reference to the VBE IDE to access reference information
' ' Hardcode adding the GUID for the VBE IDE...
ThisWorkbook.VBProject.References.AddFromguid "{00-000000000046}", 0, 0
Application.OnTime Now() + TimeValue("00:00:01"), "SetupApplicationEvents"
End Sub
In a module:
Sub SetupApplicationEvents
Set App = Application
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.

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

Create macro that updates fields before save

I'm new to VBA and am trying to write a macro that will execute a field update before the user saves the document. My first attempt was going to be intercepting the Save command like this:
Sub FileSave()
'
' FileSave Macro
' Saves the active document or template, and updates fields
'
Fields.Update
ActiveDocument.Save
End Sub
But one guide recommended not doing that and instead using DocumentBeforeSave(), so this is my new attempt:
Private Sub oApp_DocumentBeforeSave(ByVal Doc As Document, _
SaveAsUI As Boolean, Cancel As Boolean)
Fields.Update
End Sub
The first example gives me the following error:
"Runtime error '424': Object required"
The second example doesn't update the fields. I've tried the second code in both the ThisDocument object, and as a new module class. Nevertheless, the fields are still not updating when I save. On a side note, they work with this.
Private Sub Document_Close()
Fields.Update
Save
End Sub
It seems like a simple task but it just doesn't seem to work.
In your ThisDocument code module, add an Application object using the WithEvents keyword so that you can respond to its events:
Dim WithEvents TheApp As Word.Application
Add an event handler for Document_Open() that assigns your variable to the active Application object:
Private Sub Document_Open()
Set TheApp = ThisDocument.Application
End Sub
Add an event handler for Application_DocumentBeforeSave(). Within this event use the Doc object that's passed to it to update your document:
Private Sub TheApp_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
Doc.Fields.Update
End Sub
Save your document as a "Macro-Enabled Document (*.docm)".
Close the document and reopen it.
That should be it. If you want to make sure your code is working properly, add a breakpoint or a MsgBox() before your Doc.Fields.Update statement to make sure the event handler is getting called and check your values before and after the update.

Document_New event on launching Word

I have a VBA addin which I want to run every time someone opens a document. It's working fine for opening existing documents (AutoOpen) and creating new documents from the File > New menu (AutoNew) but when I just open Word up for the first time, neither of these events are firing. The only event I can seem to hook into is the AutoExec event and this is not great as the document doesn't exist yet so ActiveWindow is null.
Can anyone help?
Sub AutoNew
MsgBox "New"
End Sub
Sub AutoOpen
MsgBox "Open"
End Sub
Sub AutoExec
MsgBox "Exec"
End Sub
I would start with DocumentOpen and NewDocument. An additional level of complexity exists if you need to support ProtectedView documents; Word triggers a different event. I've found that if I try to check for that event (and it doesn't occur) it raises an error. I didn't had much luck and eventually it wasn't worth the time I was spending. I've posted an example below of some code that opens the Style Pane when a document is opened or a new one created (assuming the add-in is loading) and expands the style margin in draft view if not already expanded.
In my UI module:
Dim X As New clsAppEvent 'This is in the declarations
Public Sub OnRibbonLoad(objRibbon As IRibbonUI)
Do While Documents.Count = 0
DoEvents
Loop ' I find this useful as sometimes it seems my ribbon loads before the document.
Call Register_Event_Handler
' Other stuff
End Sub
Private Sub Register_Event_Handler()
Set X.App = Word.Application
End Sub
Then, in a class module I call clsAppEvent:
Option Explicit
Public WithEvents App As Word.Application
Private Sub App_DocumentOpen(ByVal Doc As Document)
App.TaskPanes(wdTaskPaneFormatting).visible = True
End Sub
Private Sub App_NewDocument(ByVal Doc As Document)
App.TaskPanes(wdTaskPaneFormatting).visible = True
End Sub
Private Sub App_WindowActivate(ByVal Doc As Document, ByVal Wn As Window)
If Wn.StyleAreaWidth <= 0 Then
Wn.StyleAreaWidth = 60
End If
End Sub
Other than the caveats I've noted above, the one problem I've had is if a user has Auto code in their Normal template as well. This has only come up once so I haven't investigated it.
I wish I could find the site where I learned about this (and from which the Register_Event_Handler was derived. If I find it I'll add a comment.