I'm trying to send an email based on a calendar reminder going off.
I'm having trouble getting VBA macros to recognize that an Outlook event has occurred.
I put this code in a Class Module:
Public WithEvents myOlApp As Outlook.Application
Sub Initialize_handler()
Set myOlApp = Outlook.Application 'also tried with double quotes around "Outlook.Application"
End Sub
Private Sub myOlApp_Reminder(ByVal Item As Object)
MsgBox ("test")
End Sub
Private Sub myOlApp_NewMail()
MsgBox ("test")
End Sub
When I get a new email or a reminder goes off, nothing happens.
I've tested with this macro in a normal module and it works:
Sub MsgBoxTest()
MsgBox ("test")
End Sub
I have macro settings on "Enable all macros" in the Trust Center.
I've searched google, stackoverflow, a bunch of other websites, and read the documentation on Microsoft.com.
I'm on Outlook 2016 on a PC running Windows 10 Enterprise.
A class module is just a blueprint for an object. A class module doesn't exist all by itself, at runtime a class module is just a type that an object variable can be declared as.
Your code is fine (leaked public field aside).
You're just missing an instance of that class. Keep the class and make ThisOutlookSession create an instance of it:
'[ThisOutlookSession]
Option Explicit
Private AppEvents As AppEventsHandler
Private Sub Application_Startup()
Set AppEvents = New AppEventsHandler
End Sub
Private Sub Application_Quit()
Set AppEvents = Nothing
End Sub
VBA classes fire an Initialize event on creation, and a Terminate event on destruction. Handle them to set your Private WithEvents field:
'[AppEventsHandler] (class module)
Option Explicit
Private WithEvents app As Outlook.Application
Private Sub Class_Initialize()
Set app = Outlook.Application
End Sub
Private Sub Class_Terminate()
Set app = Nothing
End Sub
Private Sub app_NewMail()
'TODO handle app event
End Sub
Private Sub app_Reminder(ByVal Item As Object)
'TODO handle app event
End Sub
'...more handlers...
That's it - now you're handling Outlook.Application events in a dedicated class, without polluting ThisOutlookSession with the nitty-gritty details of every event handler out there.
For this method, often used in documentation, run Initialize_handler manually or run it at startup in the special class module ThisOutlookSession.
Private Sub Application_Startup()
Initialize_handler
End Sub
In order to handle Reminder events, you need to enclose your code in a Sub named "Application_Reminder"
Try this:
Option Explicit
Private Sub Application_Reminder(ByVal Item As Object)
MsgBox "Test"
End Sub
Related
Right now all of my VBA code is in ThisOutlookSession. I want to put everything in a module so I can export it and other folks can import it without having to muck around with their own ThisOutlookSession. I want it to be easy for the user -- the user just imports my module file.
My code depends on Application_MAPILogonComplete and WithEvents. Neither of these are available/work in a module.
I see that classes have a Class_Initialize but it only triggers when a class object is initialized so I'd still need some kind of Application_MAPILogonComplete event.
Is there anyway to do what I want? Keep everything in a module or class that can be exported and imported that has code run when Outlook opens and supports WithEvents so I can execute a function when new e-mails are added to a folder?
Only an object can handle events, so you need a class - ThisOutlookSession is one, but if you want to modularize your code you'll need to have a class module to do that work.
Option Explicit
Private WithEvents App As Outlook.Application
Private Sub Class_Initialize()
Set App = Application
End Sub
Private Sub Class_Terminate()
Set App = Nothing
End Sub
'select "App" from the left-hand dropdown at the top of the code pane.
'then select the event(s) you want to handle from the right-hand dropdown;
'the VBE will automatically generate the handler(s) with the correct signature.
All that's left to do is to have an actual instance of that class (let's say it's called Class1) - you'll want to create that instance in ThisOutlookSession
Option Explicit
Private AppEvents As Class1
Private Sub Application_Quit()
Set AppEvents = Nothing
End Sub
Private Sub Application_Startup()
Set AppEvents = New Class1
End Sub
I would like to have a piece of code that runs when any worksheet commandbutton is pressed. I have seen similar things for userforms but can't find something that works across (and limited to) worksheet buttons.
I have got as far as creating a class module called clsCmdButton with the following code:
Private WithEvents cmdButton As MSForms.CommandButton
Private Sub cmdButton_Click()
'code i want to run
End Sub
I then think I need to loop through all worksheets, find the commandbuttons on it and intialise each as a new insance of the class. This could be done in the Workbook_Open event, however I am not sure how to code it. Could anyone help?
Thanks!
I'd amend your class like this:
Private WithEvents m_ctlButton As MSForms.CommandButton
Private Sub Class_Terminate()
Set m_ctlButton = Nothing
End Sub
Private Sub m_ctlButton_Click()
'code i want to run
End Sub
Public Property Set Button(ctlButton As MSForms.CommandButton)
Set m_ctlButton = ctlButton
End Property
Let's assume this is Class1 as I'm lazy.
Now in a new module add this:
Option Explicit
Dim colButtons As Collection
Sub hookButtons(ws As Object)
Dim oBtn As Class1
Dim obj As OLEObject
Set colButtons = New Collection
For Each obj In ws.OLEObjects
If TypeOf obj.Object Is MSForms.CommandButton Then
Set oBtn = New Class1
Set oBtn.Button = obj.Object
colButtons.Add oBtn
End If
Next
End Sub
and finally, in the ThisWorkbook module, add this:
Private Sub Workbook_Open()
hookButtons ActiveSheet
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
hookButtons Sh
End Sub
Since you can't click a button without its sheet being active, it seems easier to only hook the active sheet's buttons at any given time. Using the sheetActivate event should also mean that it gets reset more often in case of unhandled errors in other code in the workbook.
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
I have set up an application level event class to monitor when new workbooks are created/ opened by following CPearson's guide. This works fine in isolation. However, it is intended as part of add-in I'm writing where several other subs are also called in the 'Workbook_Open' sub, see below code:
Private XLApp As baseCXlEvents
Private Sub Workbook_Open()
Set XLApp = New baseCXlEvents
test
AddLocalReferences
AddModules
AddClassModules
Debug.Print "testing"
End Sub
So the XLApp variable is called in the module scope as a baseCXlEvents class. I have added a Class_Terminate event to this class and this is triggered after the Debug.print "testing" is run, i.e. XLApp is terminated after the Workbook_Open sub has run. This does not happen when I quote out the subs AddLocalReferences, AddModules, and AddClassModules, which do exactly as their names would imply. The sub test only prints a messages in debug to test whether calling additional subs caused XLApp to be terminated.
My current 'hunch' is that adding references, modules, or class modules counts as "editing", causing it to be terminated, as explained in this MS Support document. But, if so, why doesn't XLApp get terminated until the end of the sub? As opposed to as soon as AddLocalReferences is run.
Any suggestions why the class is terminated? I need it to 'stay alive' and also need to load additional modules and references for the addin upon workbook_open. If needed more details of this code can be provided.
I've decided to add my baseCXlEvents class module's code:
Option Explicit
Private WithEvents App As Application
Private Sub App_NewWorkbook(ByVal Wb As Workbook)
MsgBox "New Workbook: " & Wb.Name
End Sub
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
MsgBox "Workbook opened: " & Wb.Name
End Sub
Private Sub Class_Initialize()
Debug.Print "Initializing baseCXlEvents instance.."
Set App = Application
End Sub
Private Sub Class_Terminate()
Debug.Print "Terminating baseCXlEvents instance.."
End Sub
Well then try to separate the Workbook_Open event handler stuff (where you add references etc.) from the creation of the instance of the class baseCXlEvents using Auto_Open.
Workbook_Open runs first, Auto_Open runs then.
Note: baseCXlEvents Instancing must be set to Public.
ThisWorkbook class
Public XLApp As baseCXlEvents
Private Sub Workbook_Open()
Test
AddLocalReferences
AddModules
AddClassModules
End Sub
Standard module
Sub Auto_Open()
Set ThisWorkbook.XLApp = New baseCXlEvents
End Sub
I want to write a script that changes the format of the mail, when I am replying to a text- or rtf-mail, using Outlook 2013. To have something to begin with. I used the reply event described in the MS dev centre. Unfortunately the example does not work as I expect it to. For testing, I put in a simple message box that should pop up after clicking the reply button. I never see that message box. What did I do wrong?
Public WithEvents myItem As MailItem
Sub Initialize_Handler()
Set myItem = Application.ActiveInspector.CurrentItem
End Sub
Private Sub myItem_Reply(ByVal Response As Object, Cancel As Boolean)
'Set Response.SaveSentMessageFolder = myItem.Parent
MsgBox "I never see this message box :("
End Sub
Do you click Reply in the Explorer or Inspector? Your code will only run if you click Reply button in an Inspector.
To use the method promoted by Microsoft you need this code in ThisOutlookSession. It would be needed if the event code is not in this special class module.
Private Sub Application_Startup()
Initialize_handler
End Sub
The method described in the answer from Max, where code is in Application_Startup rather than Initialize_handler, can be used if all code is in ThisOutookSession.
you have to put this into "ThisOutlookSession" - only there it will work!
Option Explicit
Private WithEvents objInspectors As Outlook.Inspectors
Private Sub Application_Startup()
Set objInspectors = Outlook.Inspectors
end Sub
Private Sub objInspectors_NewInspector(ByVal Inspector As Inspector)
If Inspector.CurrentItem.Class = olMail Then
Set newItem = Inspector.CurrentItem
End If
Set Inspector = Nothing
End Sub
Public Sub newItem_Open(Cancel As Boolean)
newItem.BodyFormat = olFormatHTML
If newItem.Sent = True Then Exit Sub
End Sub
This will work on any new mail-item, I do not know how to make this work only for replys. You could check the subject, if there is an subject already it will be an reply.