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.
Related
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
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 :)
How to make Microsoft Word to run a VBA macro every time before any document is saved? Could it be done without adding macros into the document itself?
You can subscribe to application events in Document_Open by using WithEvents variable and conventional method names (VariableName_EventName). Works in templates as well.
You can put this code into ThisDocument object, or make a separate class module as described here.
Private WithEvents App As Word.Application
Private Sub Document_Open()
Set App = Word.Application
End Sub
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
MsgBox("BeforeSave")
End Sub
List of all application events.
You must add the code bits at the correct place.
This must be at the top of your code page amongst your public variables or constant declerations
Private WithEvents App As Word.Application
Then add this as an open document event.
Private Sub Document_Open()
Set App = Word.Application
End Sub
This is the event that fires on save command Ctrl+s or save icon. I added my own save format and print as I saw It most useful in the case of people filling out forms and you don't want them to overwrite the initial template.
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
''save file with the saveas2 command.
ActiveDocument.SaveAs2 FileName:="YourDocumentNameORVariable" + _
" Date_" + Format(Now(), "yy-mm-dd"), _
FileFormat:=wdFormatDocumentDefault, _
SaveFormsData:=True
''addition to print file upon save
ActiveDocument.PrintOut Background:=True, Range:=wdPrintAllDocument, Copies:=1, Collate:=True
End Sub
Read more about Printout methods: Microsoft VBA - PrintOut
Read more about SaveAs2: Microsoft VBA - SaveAs2
Read more about FileFormat for Saving: Microsoft VBA - FileFormat for Saving
try saving your file in .xlsm, then close, open and save it again. it should work fine.
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.
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.