How do I go about calling a userform in VBA when user clicks on the Save button in MS Word?
You have two options to do that: You can either override the built-in FileSave and FileSaveAs commands, or you can create an event handler for the application's DocumentBeforeSave event (which is a little more work to do).
Overriding the built-in commands can be accomplished by adding the following code to a VBA module (adjust the type of the user form to be displayed accordingly):
' override File -> Save
Public Sub FileSave()
CustomSave
' call ActiveDocument.Save to actually save the document
End Sub
' override File -> Save As...
Public Sub FileSaveAs()
CustomSave
' call ActiveDocument.SaveAs to actually save the document
End Sub
Sub CustomSave()
Dim frm As New frmCustomSave
frm.Show
End Sub
The second option can be implemented by placing the following code under Microsoft Word Objects -> ThisDocument in the VBA editor:
Option Explicit
Private WithEvents wdApp As Word.Application
Private Sub Document_New()
Set wdApp = Word.Application
End Sub
Private Sub Document_Open()
Set wdApp = Word.Application
End Sub
Private Sub wdApp_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
Dim frm As New frmCustomSave
frm.Show
End Sub
See Intercepting events like Save and Print for an example that should help.
Related
I'm trying to use Normal.dotm as a macro storage object analogous to the Personal.xlsb object within Excel.
The built-in Document_Close() event seems like it cannot be triggered unless it's included within a specific document's ThisDocument object.
I've also tried to use this Application_Quit() event like so but to no avail:
Private Sub Application_Quit()
Msgbox "closing word"
End Sub
Is it possible to listen for closing of the word application like it is in excel with Auto_Close(), etc?
Attempt for #BigBen based on this documentation
Class Module "Event Class Module"
Public WithEvents App As Word.Application
Normal Module "Module 1"
Dim X As New Class1
Sub Register_Event_Handler()
Set X.App = Word.Application
End Sub
Private Sub App_Quit()
MsgBox "closing word"
End Sub
There are two basic ways to capture when any Word document closes:
Use a macro named AutoClose in any module of Normal.dotm (or any template loaded as an add-in). This is the "old-fashioned" way that comes from the WordBasic (Word 2.0 and 6.0) days.
Note that if any other document (or template) has AutoClose that this will over-ride the macro running a "more general" level. In other words, the document- (or template-) specific code takes priority.
Sub AutoClose()
MsgBox "The document " & ActiveDocument.FullName & " is closing."
End Sub
Work with an application-level event. This requires both a class module and a "plain" module. The event code is in the class module; the code to initialize the class must be in a "plain" module.
This event's name is DocumentBeforeClose and can be cancelled, both by the user and by code (see the Cancel parameter in the event signature).
In contrast to AutoClose, if more than one document or template has the event running all will fire - there is no priority or "override".
Class module named clsEvents
Public WithEvents app As Word.Application
Private Sub app_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
MsgBox "The document " & Doc.FullName & " is being closed."
End Sub
Module
Public myEvents As New clsEvents
Public Sub StartEvents()
Set myEvents.app = Word.Application
End Sub
Public Sub EndEvents()
Set myEvents.app = Nothing
End Sub
Note that if both types of event are present the AutoClose fires after DocumentBeforeClose.
Note also that there is a document-specific event that will fire only for that document: Document_Close. This event must be in the ThisDocument class module of that document.
Private Sub Document_Close()
End Sub
Here are some codes I used to raise an event when the protected Word document is being closed. The purpose is to send a message box BEFORE the preview document is CLOSED. User is able to abort the closing event, modify the document and close the document again. The simpler Word document Document_Close() event handler does not support CANCEL = TRUE.
Step 1. Add following code into a CLASS (codes must be in class module). I named the class as 'EventClassModule', I made a public declaration 'App', these names are referenced in the module.
Public WithEvents App As Word.Application
Private Sub App_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
If vbYes = MsgBox("Do you need to modify the certificate?", vbYesNo) Then
If ActiveDocument.ProtectionType <> wdNoProtection Then
ActiveDocument.Unprotect
End If
Cancel = True
End If
End Sub
Step 2: Add following code into a module. The class name 'EventClassModule' and the public declaration 'App' are referenced here. The code 'Set X.App = Word.Application' will point to the Word application object, and the event procedures in the class module will run when the events occur.
Dim X As New EventClassModule
Sub abc()
Set X.App = Word.Application
Dim Doc As Word.Document
Set Doc = ActiveDocument
If Doc.ProtectionType <> wdNoProtection Then
Doc.Unprotect
End If
ActiveDocument.Range.Text = Time
Doc.Protect wdAllowOnlyReading
End Sub
Is this a Word 2003 VBA bug? DocumentBeforePrint executes multiple times?
I reference
http://msdn.microsoft.com/en-us/library/office/aa211873(v=office.11).aspx
http://msdn.microsoft.com/en-us/library/office/aa211915(v=office.11).aspx
DocumentBeforeClose syntax
I make a test.dot with macro DocumentBeforePrint.
'ref http://msdn.microsoft.com/en-us/library/office/aa211915(v=office.11).aspx
Dim X As New EventClassModule
Sub Register_Event_Handler()
Set X.appWord = Word.Application
End Sub
Private Sub Document_New()
Call Register_Event_Handler
End Sub
Private Sub Document_Open()
Call Register_Event_Handler
End Sub
'ref http://msdn.microsoft.com/en-us/library/office/aa211873(v=office.11).aspx
Public WithEvents appWord As Word.Application
Private Sub appWord_DocumentBeforePrint _
(ByVal Doc As Document, _
Cancel As Boolean)
MsgBox "WORKING!"
End Sub
When I press Ctrl+P, the macro executes.
There is a bug. When I double click test.dot to generate two word files (a.doc/b.doc).
Press Ctrl+P,the DocumentBeforePrint will execute twice.
If I generate 3 word files, Press Ctrl+P, the Macro will execute three times.
What's wrong? I just want to execude once.
It appears you have put all the code except the printing code in the ThisDocument code module. This is incorrect. Because you put ^Dim X as NewEventClassModuleinThisDocument`, which is, itself, a class module representing each document, multiple instances were being created. Declaring it in a "plain module" doesn't cause this problem.
You need three modules:
"Plain" module:
Dim X As New EventClassModule
Sub Register_Event_Handler()
Set X.appWord = Word.Application
End Sub
"ThisDocument" module:
Private Sub Document_New()
Register_Event_Handler
End Sub
Private Sub Document_Open()
Register_Event_Handler
End Sub
"EventClassModule"
Public WithEvents appWord As Word.Application
Private Sub appWord_DocumentBeforePrint _
(ByVal Doc As Document, _
Cancel As Boolean)
MsgBox "WORKING!"
End Sub
I want to detect when user press ctrl-S or click on Save option of Microsoft word using VBA excel macro code.
I found related links about Save changes while closing and Detecting if a document is getting close but I not able to find some example code for detecting saving of word document.
Any help would greatly appreciated.
Thanks
Unfortunately (or fortunately) Word does not work like excel and there the keybinding follows different logic. Thus, in Word, you should try something like this, to bind Ctrl + S. The idea is that on openning of the file you tell the application to bind Ctrl + S to the SaveMe Sub.
Option Explicit
Private Sub Document_Open()
With Application
.CustomizationContext = ThisDocument
.KeyBindings.Add KeyCode:=BuildKeyCode(wdKeyControl, wdKeyS), KeyCategory:=wdKeyCategoryCommand, Command:="SaveMe"
End With
End Sub
Public Sub SaveMe()
MsgBox "User Saved"
End Sub
Put the code in ThisDocument:
Until now, it works this way only for Ctrl+S. If you go for the Save through the menu, it will not follow this logic. This is what you should do then:
For the general solution, follow these instructions:
I. Create a clsWord and put the following code inside:
Option Explicit
Public WithEvents appWord As Word.Application
Private Sub appWord_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
Call SaveMe
End Sub
II. Change the code in ThisDocument to this:
Option Explicit
Dim myWord As New clsWord
Private Sub Document_Open()
With Application
.CustomizationContext = ThisDocument
.KeyBindings.Add KeyCode:=BuildKeyCode(wdKeyControl, wdKeyS), _
KeyCategory:=wdKeyCategoryCommand, Command:="SaveMe"
End With
Set myWord.appWord = Word.Application
End Sub
III. In a module:
Option Explicit
Public Sub SaveMe()
MsgBox "User Saved"
End Sub
Parts of the ideas are taken from the MSDN here - https://msdn.microsoft.com/en-us/library/office/ff838299.aspx But the code there was not complete :)
I've ended up cleaning up someone's mess on a Word document that's used throughout my company. It is a macro-heavy document that needs to be saved as a .docm exclusively. I'm worried about it getting "save as"-d as something other than a .docm, but I can't seem to find a way to limit the save as file picker or swap out the extension on save as while still using VBA.
How can I achieve this?
Edit: Some of the things I've tried, to no avail:
This has the right idea, but doesn't actually limit the filetypes down on save https://answers.microsoft.com/en-us/msoffice/forum/msoffice_word-mso_other/how-to-set-path-for-wddialogfilesaveas-dialog/535b7f9c-9972-425c-8483-35387a97d61d
Towards the bottom, Microsoft says that SaveAs isn't compatible with filter.clear and filter.add https://msdn.microsoft.com/en-us/library/office/aa219834(v=office.11).aspx
In order to hijack the native "SaveAs" dialog in Word, you need to lever the Application-level event for DocumentBeforeSave, and then call the FileDialog manually, in order to validate the extension.
1. Create a standard code module and name it modEventHandler. Put the following code in it.
Option Explicit
Public TrapFlag As Boolean
Public cWordObject As New cEventClass
'You may not need these in a DOCM, but I needed to implement this in an ADD-IN
Sub TrapEvents()
If TrapFlag Then
Exit Sub
End If
Set cWordObject.DOCEvent = Application
TrapFlag = True
End Sub
Sub ReleaseTrap()
If TrapFlag Then
Set cWordObject.DOCEvent = Nothing
Set cWordObject = Nothing
TrapFlag = False
End If
End Sub
2. Create a Class Module called cEventClass and put this code in the module:
Option Explicit
Public WithEvents DOCEvent As Application
Private Sub DOCEvent_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
' Do not prevent SAVEAS for *other* documents
If ObjPtr(Doc) <> ObjPtr(ThisDocument) Then
Exit Sub
End If
If SaveAsUI Then
' The user has invoked SAVE AS command , so we will hijack this and use our own FileDialog
Call CustomSaveAs(Doc)
' Prevent duplicate appearance of the SAVEAS FileDialog
Cancel = True
End If
End Sub
Private Sub CustomSaveAs(ByRef Doc As Document)
Dim fd As FileDialog
Dim filename$
Set fd = Application.FileDialog(msoFileDialogSaveAs)
fd.Show
If fd.SelectedItems.Count = 0 Then Exit Sub
filename = fd.SelectedItems(1)
If Not Right(filename, 4) = "docm" Then
' ### DO NOT EXECUTE this dialog unless it matches our .DOCM file extension
MsgBox "This document should only be saved as a DOCM / Macro-Enabled Document", vbCritical, "NOT SAVED!"
Else
fd.Execute
End If
End Sub
3. In ThisDocument module, do the following code:
Private Sub Document_Close()
Call modEventHandler.ReleaseTrap
End Sub
Private Sub Document_Open()
Call modEventHandler.TrapEvents
End Sub
How Does This Work?
ThisDocument raises the Document_Open event which calls on the TrapEvents procedure.
TrapEvents procedure creates a WithEvents instance of Word.Application class, exposing additional events to automation. One of these is DocumentBeforeSave.
We use the DocumentBeforeSave event to trap the SaveAs operation. If the User has requested a SaveAs, then we force the dialog with logic as created in CustomSaveAs procedure. This uses simple logic to test the file extension provided, and prevents the document from being saved if it is not a DOCM extension. If the FileDialog does receive a valid DOCM extension, then we Execute the dialog which saves the file as the new name.
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