I want to enable another function "code" only if the document has been printed already, i was thinking something along the lines of
Sub Testing
Dim hasPrinted as boolean
If ActiveDocument.PrintOut = True Then
hasPrinted = True
call code here...
Else
hasPrinted = False
MsgBox "Please Print Before Adding"
End If
End Sub
i receive an error that says "Compile Error, expected function or variable" on the "ActiveDocument.PrintOut" line. Could anyone give me some directions?
Capturing the print events is not an easy job in Word VBA. However here is a neat trick :)
For this do the following
Create a class module say Class1 and paste this code
Option Explicit
Public WithEvents oApp As Word.Application
Private Sub oApp_DocumentBeforePrint(ByVal Doc As Document, Cancel As Boolean)
ActiveDocument.Bookmarks("DocWasPrinted").Delete
With ActiveDocument.Bookmarks
.Add Range:=Selection.Range, Name:="DocWasPrinted"
.DefaultSorting = wdSortByName
.ShowHidden = True
End With
End Sub
Now insert a module and paste this code
Option Explicit
Dim oAppClass As New Class1
Public Sub AutoExec()
Set oAppClass.oApp = Word.Application
End Sub
Sub Testing()
If hasPrinted = True Then
MsgBox "Document was printed"
'~~> Call your code
Else
MsgBox "Please Print Before Adding"
End If
End Sub
Function hasPrinted() As Boolean
If ActiveDocument.Bookmarks.Exists("DocWasPrinted") = True Then
hasPrinted = True
End If
End Function
Close your document and reopen it. Now test it.
LOGIC:
What this code does is the moment the user prints the document, the code creates a hidden bookmark called DocWasPrinted And in my code I check if the bookmark was created or not.
Remember to delete the bookmark on Document Exit.
Private Sub Document_Close()
ActiveDocument.Bookmarks("DoWasPrinted").Delete
End Sub
This question provides information about creating a make-shift Document After Print event.
Once you've done that, you can have a boolean value updated to true to indicate the document has printed. Word does not store this information natively.
Related
Dear Readers
I am trying to make individual Serial numbers ( incrementing number) for some Forms in Microsoft Word, so we can track each one of them much simpler between people.
I used this link and it did work,
but it needs always running Macro and I couldn't figure out how to make it automatic with just a simple Ctrl+P shortcut, so I used this second link for that reason,
finally, it looked so great but there is a problem since the second link is a just "before Print" code, there is always one extra print at the end since the Printing process starts exactly after macro ended. any cancel print process code out there?
how can I overcome this one?
Codes under the document
Private Sub Document_Open()
Register_Event_Handler
End Sub
Codes under the Module
Dim X As New EventClassModule
Sub Register_Event_Handler()
Set X.App = Word.Application
End Sub
Codes under the class
Public WithEvents App As Word.Application
Private Sub App_DocumentBeforePrint(ByVal Doc As Document, Cancel As Boolean)
' Run code directly inside this Sub OR
MsgBox "Before Print"
' Call another Sub here, note, Sub and Module name can't match
Call FilePrint
' See https://www.freesoftwareservers.com/wiki/compile-error-expected-variable-or-procedure-not-module-macros-microsoft-office-29982732.html
End Sub
and finally Codes of FilePrint section as a Module
Sub FilePrint()
Dim i As Long, j As Long
With ActiveDocument
j = CLng(InputBox("How many copies to print?", "Print Copies"))
For i = 1 To j
With .CustomDocumentProperties("Counter")
.Value = .Value + 1
End With
.Fields.Update
ActiveDocument.PrintOut Copies:=1
Next
.Save
End With
End Sub
I'm facing an annoying issue where I simply want to schedule an asynchronous macro from another instance of Word, which happens to be the same .doc file.
Meaning, in the ThisDocument namespace I have the following code snippet:
Public Sub Document_Open()
Set Obj = New Word.Application
Obj.OnTime Now + TimeValue("00:00:01"), "Module1.Test"
End Sub
I've declared a new object of Word due to the following reasons:
My macro may block user's I\O
The user may close the document before the macro finishes its task
And declared a module named Module1 with a simple MsgBox
Public Sub Test()
MsgBox "hhh"
End Sub
Needless to say, nothing happened, and I'm unable to check what OnTime function returns.
I've also tried the following combinations:
"!Module1.Test"
"c:\\....\\file.doc!Module1.Test"
What am I missing here?
Your newly created Word application, represented by a Word.Application object, doesn't have the document open. Thus there's no "Module1" as far as he's aware. The fact that you want to run code from the same document is immaterial. It's a different instance of Word.
Something like this works:
'''''''' ThisDocument
Option Explicit
Private Sub Document_Open()
Dim res As VbMsgBoxResult
Dim Obj As Word.Application
If Application.Visible Then
res = MsgBox("Hello", vbOKCancel, "Hi!")
Else
Exit Sub
End If
If res = vbOK Then
Set Obj = New Word.Application
Obj.Documents.Open "C:\Users\conio\Desktop\Hello.docm", , True
Obj.OnTime Now + TimeValue("00:00:01"), "Module1.Foo"
End If
End Sub
'''''''' Module1
Public Sub Foo()
If Not Application.Visible Then
MsgBox "Foo"
Application.Quit
End If
End Sub
Maybe you'd want to use a different check to differentiate between the interactive run and the unattended run.
When I start my Userform, I first chack for a value. If this value is not existing the userform should close.
Try1: UserForm_Inizialize
Public Sub UserForm_Initialize()
Call languagePack
'
'initialize the userform
'
End Sub
Try1: function to choose a languagepack
Private Sub languagePack()
Dim LanguageItems(45) As String
Dim Language_ID As Integer
Language_ID = Outlook.LanguageSettings.LanguageID(msoLanguageIDUI)
Call Language_AS.getLanguage(Language_ID, LanguageItems)
If Not LanguageItems(0) = "" Then
With Me
'--write the array items into the userform objects and vaues
End With
Else
MsgBox "It doesn't exist a Language-Pack for your language! Pleas change to english."
Unload Advanced_Search ' will not work
End If
End Sub
Try1 was to unload the userform in the function languagePack(), but didnt stop run and I get an error. So I tried another thing:
Try2: UserForm_Inizialize
Private close_userform As Boolean
Public Sub UserForm_Initialize()
Call languagePack
If close_userform = Flase Then
'
'initialize the userform
'
else
Unload Advanced_Search ' will not work
end if
End Sub
Try2: function to choose a languagepack
Private Sub languagePack()
Dim LanguageItems(45) As String
Dim Language_ID As Integer
Language_ID = Outlook.LanguageSettings.LanguageID(msoLanguageIDUI)
Call Language_AS.getLanguage(Language_ID, LanguageItems)
If Not LanguageItems(0) = "" Then
With Me
'
'--write the array items into the userform objects and vaues
'
End With
close_userform = False
Else
MsgBox "It doesn't exist a Language-Pack for your language! Pleas change to english."
close_userform = True
End If
End Sub
Whats wrong on Try2? The Boolean close_userform is global so both functions can read the value. But if it reachs the unload it happen nothing. After reaching the end sub I get an error.
The error is: Run-time error '91':
Objective variable or With block variable not set
Macro that starts the Userform
Sub start_Advanced_Search()
Advanced_Search.Show (vbModeless)
End Sub
Pleas help me. Thanks for every command an answer. Kind regards, Nico
Why not make the languagePack sub a function that returns a Boolean instead of writing the result to close_userform. Next use Unload Me instead Unload Advanced_Search
That’s the solution of my problem.
Thanks Tom for your help.
Description:
If there is no language pack for my Userform (I make the language packs), it will close the userform.
The function languagePack() returns a boolean (ture = language pack exists | false = no language pack exists).
This boolean is saved in hasLanguage. With the function getHasLanguage() I can get the value outside of the userform.
This function is used in the sub start_Advanced_Search. With the if function I check if there is a language pack, if not it will unload the userform.
Userform
Private hasLanguage As Boolean
Public Sub UserForm_Initialize()
hasLanguage = languagePack()
If hasLanguage Then
'
'set the defaults...
'
End If
End Sub
Public Function getHasLanguage()
getHasLanguage = hasLanguage
End Function
Private Function languagePack() As Boolean
'array to save the new language
Dim LanguageItems(49) As String
'this value will contain the LanguageID of Outlook
Dim language_ID As Integer
'get LanguageID of Outlook
language_ID = Outlook.LanguageSettings.LanguageID(msoLanguageIDUI)
'call a sub to get the language
Call Language_AS.getLanguage(language_ID, LanguageItems)
'there is a languagepack if the first element of "LanguageItems" is not ""
If Not LanguageItems(0) = "" Then
With Me
'
'set the language of the userform
'
End With
languagePack = True
Else
'there is no languagepack
MsgBox "It doesn't exist a Language-Pack for your language! Pleas change to english."
languagePack = False
End If
End Function
Modul
Sub start_Advanced_Search()
'start the userform
Advanced_Search.Show (vbModeless)
'use the get function
If Not Advanced_Search.getHasLanguage() Then
'unload if flase
Unload Advanced_Search
End If
End Sub
I've got a workbook which runs a macro to show the userform Open1 as it opens, using the (very basic) code:
Private Sub Workbook_Open()
Open1.Show
End Sub
This does its job fine - each time I open the workbook, the userform pops up and runs perfectly.
But, I want the userform to appear the first time the workbook is opened only. Is there a way to allow this to happen?
You could use a dummy module which gets deleted the first time you open the spreadsheet...
Something like:
If ModuleExists("DummyModule") Then
Open1.Show
DoCmd.DeleteObject acModule, "DummyModule"
End If
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In CurrentProject.AllModules
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Update: as stated, DoCmd isn't used in excel vba. That will teach me to write code without testing it!
The following updated code will work, but in order to access the VB environment, excel needs to be trusted.
There is a setting in the Trust Center>Macro Settings that you can tick for this code to work under Developer Macro Settings
As such, this may not be the way to go as it opens up the possibility of security issues...
Sub RemoveModule()
If ModuleExists("DummyModule") Then
Open1.Show
Dim vbCom As Object: Set vbCom = Application.VBE.ActiveVBProject.VBComponents
vbCom.Remove VBComponent:=vbCom.Item("DummyModule")
End If
End Sub
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In Application.VBE.ActiveVBProject.VBComponents
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Try this:
If Sheets("Hide").Cells(1,1) = "1" Then
Open1.Show
Sheets("Hide").Cells(1,1) = "0"
End if
You must create the sheet Hide, and give the cell A1 the value 1, in that case the form will be shown.
After you create the sheet, hide it with this
Sheets("Hide").Visible = xlVeryHidden
And show it with this
Sheets("Hide").Visible = True
Here's an alternative bit of code that will persist between saves and allow you to reset it. No need to create a hidden sheet.
Put this in a module (invoke the DisplayFormIfFirstTime from your Workbook_Open event handler....)
Option Explicit
Private Const cMoniker As String = "FormHasBeenDisplayed"
Private Sub DisplayFormIfFirstTime()
If HasBeenOpened = False Then DisplayForm
End Sub
Public Sub DisplayForm()
MsgBox "Ok, its not a form but a dialog box...", vbInformation
End Sub
Public Function HasBeenOpened() As Boolean
Dim oName As Name
On Error Resume Next
Set oName = Application.Names(cMoniker)
On Error GoTo 0
If Not oName Is Nothing Then
HasBeenOpened = True
Else
Call Application.Names.Add(cMoniker, True, False)
End If
End Function
'Call this to remove the flag...
Public Sub ResetOpenOnce()
On Error Resume Next
Application.Names(cMoniker).Delete
End Sub
Based on the idea supplied by PaulG, I have coded an upgrade that will check for the name and if not found run a function, add the name and save the workbook for a more seemless approach to this problem...
Placed in ThisWorkbook
Private Sub Workbook_Open()
Run "RunOnce"
End Sub
Placed in a module
Sub RunOnce()
Dim Flag As Boolean: Flag = False
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then Flag = True
Next
If Flag = False Then
Call Application.Names.Add("FunctionHasRun", True, False)
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
Call RunOnceFunction
End If
End Sub
Private Function RunOnceFunction()
Open1.Show
End Function
Sub ResetRunOnce()
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then
Application.Names.Item("FunctionHasRun").Delete
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
End If
Next
End Sub
I'm writing a custom Word template which does some processing upon saving the current document.
In Office2007, to save a document you can use the Save and the Save As functions, and handle the events with the FileSave and FileSaveAs macros.
But when the user hovers over the SaveAs option, other sub-options are displayed: Save as a document, as a Word template, as Word 97-2003 document, etc. These sub-options don't seem to have their own events, but I'd like to know when the user uses them.
So I came up with the idea to use the DocumentBeforeSave event, but then I still have to figure out if the save occured with the standard Save/SaveAs options or with the sub-options.
I thought about setting a variable to True in the Save/SaveAs functions, which the DocumentBeforeSave event would check to see if one of the normal save methods occured, then it would set the variable back to False.
But after experimenting with different methods, I can't figure out how I can pass the value of a variable between ThisDocument and the Class Module which has the BeforeSave event.
Any ideas? Thanks!
Edit: Example code that doesn't work:
ThisDocument:
Public pSSave As Boolean
Public Property Get SSave() As Boolean
SSave = pSSave
End Property
Public Property Let SSave(Value As Boolean)
pSSave = Value
End Property
Sub FileSave()
Me.SSave = True
If SSave = True Then
MsgBox "True"
End If
Application.ActiveDocument.Save
If SSave = True Then
MsgBox "True"
End If
End Sub
Class Module:
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
If Application.ActiveDocument.SSave = False Then
MsgBox "False"
End If
End Sub
The class module registration is done properly, I won't paste the code her.
The result displayed is True, False, True while theoretically, it should be True, True.
I still miss something in your logic. In comments I thought about different-reverse logic which would go this way. This code below is a mix of my way and code you presented.
Class Module
Public WithEvents App As Word.Application
Public pSSave As Boolean 'your class variable/property
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
If pSSave = False Then
MsgBox pSSave
Else
MsgBox pSSave
End If
End Sub
Module1
'class initialization
Public wrdAPP As New myClass
Sub set_References()
Set wrdAPP.App = Application
End Sub
ThisDocument Module
Private Sub Document_Open()
'to initialize public variable when open
Call Module1.set_References
End Sub
Sub FileSave()
wrdAPP.pSSave = True
Application.ActiveDocument.Save
If wrdAPP.pSSave = True Then
MsgBox "True"
End If
End Sub
I don't know which way you are going to run FileSave sub. But after it is run it pass the value to class property which you could check in your event.
Hope it would help you anyway.