Previously I have used this:
Private Sub Document_Close()
Me.Saved = True
End Sub
to disable the save prompt when exiting a Word document and changes have been made, however I have added a "Combo Box (ActiveX Control)", and now Word is prompting to save again. Is there a way around this?
I've tried writing code to just delete the Combo Box when the document closes, but the box deletes itself after being used (not my code, it just does), then when the document closes the box doesn't exist and causes an error there. I could just do an if exist/error control, but I feel like that is just getting sloppy instead of finding the root problem... can someone help?
If had the same issue, and found the solution with Bing:
http://answers.microsoft.com/en-us/office/forum/office_2007-customize/activex-control-blocks-ms-word-vba/71eca664-8e43-4e4f-84c5-59154661ee5a
The following code helped me to get around this issue:
Dim WithEvents App As Application
Private Sub App_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
'Did the user saved our file?
If Not ThisDocument.Saved Then
'Close our file?
If Doc.Name = ThisDocument.Name Then
'Cancel the dialog always!
Cancel = True
'Set the save state
ThisDocument.Saved = True
'Close the document after this event
Application.OnTime Now + TimeSerial(0, 0, 1), Me.CodeName & ".Document_AfterClose"
End If
End If
End Sub
Sub Document_AfterClose()
'Close the document without saving
ThisDocument.Close False
End Sub
Private Sub cboEquip_Change()
'Let the document know that a change is made
ThisDocument.Saved = False
End Sub
Private Sub Document_Open()
Set App = Application
End Sub
Related
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.
I have an automated task that will open Excel and run some VBA code at a specific time every morning. If a user has the excel file open I want to warn them the file will be automatically closed in a few moments via a popup message, then shortly thereafter have Excel automatically save and quit. In case the user is away from their computer, I want the popup message to close automatically after 5 seconds so it doesn't freeze my SaveExit subroutine.
The popup message is appearing, but it will not close automatically.
It is only closing if "OK" is clicked. This is preventing the SaveExit routine from running.
Under Microsoft Excel Objects in This Workbook:
Private Sub Workbook_Open()
ThisWorkbook.Worksheets("Pull Data").Activate
Application.OnTime TimeValue("1:00:00 AM"), "CloseWarn", TimeValue("1:02:00 AM")
Application.OnTime TimeValue("1:03:00 AM"), "SaveExit", TimeValue("1:04:00 AM")
End Sub
In a module called autoclose:
Sub SaveExit()
Application.DisplayAlerts = False
With ThisWorkbook
.Save
Application.Quit
End With
End Sub
Sub CloseWarn()
Select Case CreateObject("WScript.Shell").PopUp("closing soon", 5, "Warning")
Case 1, -1
End Select
End Sub
How do I get a popup warning message to display at a specific time, then close if no user input occurs??
Figured it out! Thanks #Evan for pointing me in the right direction. I don't understand why, but when using the Application.OnTime command under Workbook_Open, things work better if I identify the module where the routine is located. After I figured this out I moved my subs to ThisWorkbook to streamline my code. I created a userform called CloseAlert that informs users that Excel is about to close automatically. The userform closes after 5 seconds, or upon user clicking "OK".
Under Microsoft Excel Objects in ThisWorkbook:
Private Sub Workbook_Open()
ThisWorkbook.Worksheets("Pull Data").Activate
Application.OnTime TimeValue("16:08:00"), "ThisWorkbook.ShowCloseAlert"
Application.OnTime TimeValue("16:09:00"), "ThisWorkbook.SaveExit"
End Sub
Private Sub ShowCloseAlert()
CloseAlert.Show vbModeless
Application.OnTime Now + TimeValue("00:00:05"), "Unloadit"
End Sub
Sub SaveExit()
Application.DisplayAlerts = False
With ThisWorkbook
.Save
Application.Quit
End With
End Sub
In a separate module called unload_CloseAlert:
Public Sub Unloadit()
unload CloseAlert
End Sub
I want to add this code to ThisDocument in my Word .docm file.
Sub AutoClose()
ActiveDocument.Saved = False
End Sub
The code prevents the "Do you want to save?" dialog from appearing. I only want to add this however after a certain click event in the document, because I also have Save As and Save disabled, and if I add all three, then I wouldn't be able to save the document myself. (In fact, I can't add all three, for that reason.)
If I add the above code only to be active after the last click event in the document however it should be fine, since then I can still save changes I make, as long as I haven't clicked that trigger at the end yet. Preventing people from closing and getting the "Do you want to save?" dialog at the end is the most important to me.
is there a way to write the above code to ThisDocument when another click event fires? Or put another way, is there any way to have a click event put the above code into effect?
Here's the sub at the end that I'd want to have trigger the above code to (activate? Be written? Be enabled? Be uncommented?)
Private Sub formatSaveB_Click()
ActiveDocument.Bookmarks("mark3").Select
Selection.Delete
ActiveDocument.InlineShapes(1).Delete
With Dialogs(wdDialogFileSaveAs)
.Format = wdFormatFilteredHTML
.Show
End With
End Sub
So after that event happens, I want
Sub AutoClose()
ActiveDocument.Saved = False
End Sub
to be active in ThisDocument -- but not before that event.
Programatic access to the VBA project will need to be enabled if it isn't already. However, to add the sub you described after the other code runs add this code to your sub:
ThisDocument.VBProject.VBComponents("ThisDocument").CodeModule.AddFromString "Sub AutoClose(): ActiveDocument.Saved = False: End Sub"
That should do the trick. You may have to tinker a little, I can't test fully because I'm in an environment where programatic access to the VBA project is disabled, but that should do what it sounds like you want to do.
Slight edit: If you want to prevent the "Do you want to save changes you made ... " message when closing word you would need to do this:
ThisDocument.VBProject.VBComponents("ThisDocument").CodeModule.AddFromString "Private Sub Document_Close(): ActiveDocument.Saved = True: End Sub"
Which will prevent that message from displaying when closing the document.
I can't completely follow your logic but I think what you're asking is something like this:
ThisDocument module
Public event1 As Boolean
Public event2 As Boolean
Sub AutoOpen()
event1 = False
event2 = False
End Sub
Event Procedures
Private Sub formatSaveB_Click()
'// Event code here...
event1 = True
End Sub
Private Sub otherEvent_Click()
'// Event code here...
event2 = True
End Sub
Then in your final sub
Sub AutoClose()
'// Saved state only set to true if previous 2 events have been executed.
ActiveDocument.Saved = (event1 And event2)
End Sub
Set a flag in the event handler you want to disable the dialog, then test it when you exit. Note that you have to check for either the flag or if the document is already saved to avoid being prompted if the document actually has been saved but you haven't triggered formatSaveB_Click(). Assumes that formatSaveB_Click() is in ThisDocument.
In ThisDocument:
Private clicked As Boolean
Sub AutoClose()
ActiveDocument.Saved = clicked Or ActiveDocument.Saved
End Sub
Private Sub formatSaveB_Click()
ActiveDocument.Bookmarks("mark3").Select
Selection.Delete
ActiveDocument.InlineShapes(1).Delete
With Dialogs(wdDialogFileSaveAs)
.Format = wdFormatFilteredHTML
.Show
End With
'Set your flag here.
clicked = True
End Sub
Okay adding this line from MattB did the trick, after I discovered that his suggestion had a typo, and I deleted the extra letter :)
ThisDocument.VBProject.VBCompontents("ThisDocument").CodeModule.AddFromString "Private Sub Document_Close(): ActiveDocument.Saved = True: End Sub"
I was just pasting it in to try it like the others, and it had VBCompontents which I didn't spot until just now going over this again, and once I deleted the extra t, bingo.
Just what I needed. Thanks a bunch.
Q: What is recommended way of notifing user about loss of functionality unless User change macro security settings?
What I do now:
I display warning on the first sheet user see after opening Workbook, with explanation why things WONT work unless proper settings are set.
And I hide it on start up. (Which wont happen unless settings are OK)
But its not perfect solution:
That message is just one time. (While user could send that sheet to somebody else with different settings...)
Hiding and showing those few rows is treaded by Excel as changing document. (So just opening and closing excel will generate Save changed warning!)
Ok, here is best answer I could think off
(And big thx to #brettdj for suggestion!)
Sub HideMacroSecWarning()
Status = ThisWorkbook.Saved
Help.Range("Warning").rows.hidden = True
ThisWorkbook.Saved = Status
End Sub
Sub ShowMacroSecWarning()
Status = ThisWorkbook.Saved
Help.Range("Warning").rows.hidden = False
ThisWorkbook.Saved = Status
End Sub
Those are for showing and hiding macros. Saved state is preserved so only user actions can trigger "unsaved changes" dialogbox.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call ShowMacroSecWarning
End Sub
Private Sub Workbook_Open()
Call HideMacroSecWarning
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call ShowMacroSecWarning
End Sub
Private Sub Workbook_AfterSave(ByVal Success As Boolean)
Call HideMacroSecWarning
End Sub
Those assure that no matter the state of user settings warning will ALWAYS be displayed in SAVED FILE. (So even if for user Exel do not display that warning, somebody else who will open it on different machine will see that warning.)
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.