Assuming I have:
a word template including macro: custom_template.dotm;
two bookmarks in this template: 'first_name" and "last_name".
I would like that on "Save" event, in the dialog box, the application proposes to user, instead of "document1", and only if relative bookmarks exist, the filename "Document of first_name second_name.docx".
Can anybody explain me how to achieve this with VBA?
Thanks.
=== UPDATE ===
Now I've this code, working well when I execute it.
I would like it runs automatically when user clicks on "save document".
Sub Demo()
Dim sFlNm As String
With ActiveDocument
sFlNm = "Document of " & .Bookmarks("first_name").Range.Text & " " & .Bookmarks("last_name").Range.Text
End With
With Dialogs(wdDialogFileSaveAs)
.Name = sFlNm
.Show
End With
End Sub
For a macro to run at the client's end, you would have to send a macro-enabled template or document. Many people who are running anti-virus software will get a warning of a possible Word virus. Then the user will have to manually enable the macros. Are they going to bother?
Making it run automatically with a Save command may have unintended consequences. You'll have to check whether the bookmarks have actually been filled, and using the Save command while you're revising the document can save it with a new file name. But you asked, so here's how: rename the macro as FileSave. Then when you choose Ctrl + S or File>Save in Word, the dialog will automatically pop up:
Sub FileSave()
Dim sFlNm As String
With ActiveDocument
sFlNm = "Document of " & .Bookmarks("first_name").Range.Text & " " & .Bookmarks("last_name").Range.Text
End With
With Dialogs(wdDialogFileSaveAs)
.Name = sFlNm
.Show
End With
End Sub
In Word, to create a macro that runs automatically when you choose a Word command, follow these steps:
Choose Developer>Macros.
Change the Macros in dropdown to Word commands.
Choose the command name you want to re-purpose.
Change the Macros in dropdown back to the macro-enabled document or template that you're developing.
Click on the Create button. A new macro is created in the VBE with the correct command name. Fill in the macro with whatever you want the macro to do.
Related
Previous versions of this code had no message box, which sometimes resulted in the wrong workbook being closed. I added an okcancel message box to keep this from happening, but the message box doesn't show up when I use a shortcut key to open it. What am I missing?
Sub openerQuick()
Dim myfile As String
Dim clientID As String
Dim PDSfilename As String
Dim myopener As Variant
clientID = ActiveCell
PDSfilename = ActiveCell.Offset(0, 1)
myfile = "N:\DOWNLOAD\FILEDIR\" & clientID & "\original\" & PDSfilename
Set wbOpener = Workbooks.Open(myfile)
If MsgBox("Okay to close?", vbOKCancel) = vbOK Then
ActiveWorkbook.Close
End If
End Sub
I doubt the MsgBox itself has anything to do with losing the macro shortcut key.
Shortcut keys are defined by a hidden member attribute value, and the VBE has a tendency to lose member attributes when you rewrite a method's signature, or rewrite a module*; it's possible that modifying the code caused the previously existing attribute to somehow get lost.
Remove the module from the project, pick "Yes" when prompted whether to export or not
Open the exported file in Notepad++ your favorite text editor
Locate the procedure
Add the attribute if it's not there
Save the file if it was changed, re-import into the project
The member attribute should look something like this:
Public Sub OpenerQuick()
Attribute OpenerQuick.VB_ProcData.VB_Invoke_Func = "A\n14"
'...code....
End Sub
That exact attribute associates Ctrl+Shift+A to the macro; change the A for whichever letter rocks your boat to change the shortcut.
When you record a macro in Excel and specify A for a shortcut key, the macro recorder automatically adds this hidden attribute for you.
* Rubberduck's module rewriters have that very problem and it's driving me nuts.
In a module write the following 2 subs:
Public Sub OpenerQuick()
If MsgBox("Okay to close?", vbOKCancel) = vbOK Then ActiveWorkbook.Close
End Sub
Public Sub InitiateMe()
Application.OnKey "{a}", "OpenerQuick"
End Sub
Run only InitiateMe. Now, when you press a, InitiateMe would be triggered.
Ok so let me explain in detail.
Suppose there is a word file called "Word.doc"
What I want to do is basically use VB.NET for doing the following things :
Open the word document
Add a macro code
For e.g
Add the following macro code to the Word Document
Sub AutoOpen()
Msgbox
End Sub
And then save this document.
Just remember that I want to insert macro code to a word document not retreive an already present macro code from a document
Here is a simple VBA macro that shows how to use the Word object model to add a macro to a document (VB.NET code will be almost identical).
Sub NewDocWithCode()
Dim doc As Document
Set doc = Application.Documents.Add
doc.VBProject.VBComponents("ThisDocument").CodeModule.AddFromString _
"Sub AutoOpen()" & vbLf & _
" MsgBox ""It works""" & vbLf & _
"End Sub"
End Sub
Note that running this code requires that access to the VBA project object model is trusted (this needs to be enabled in the trust center in the the Word options).
Working with objects in the VBA Editor through the object model requires a reference to the Microsoft Office VBA Extensibility 5.3 object model which you can find in the COM tab of Project/Add References in Visual Studio.
I like to add an Imports statement to the top of the code so that I don't have to always write out the full namespace qualification:
Imports VBE = Microsoft.Vbe.Interop
An AutoOpen macro needs to be a "public" Sub in a normal code module. Assuming you want to add a new code module to the document, use the VBComponents.Add method and specify the enumeration type vbext_ct_StdModule.
By default, the VBE will name the new module "Module#" - an incrementing number. If you ever need to programmatically address this module again (to see if it exists, for example) it would probably be better to assign a name to it.
Code as a string is added using the AddFromString method.
If you're adding code to a document the possibility exists that the document is of type docx, meaning it cannot contain macro code. A document must have the extension docm in order to contain macro code. So you may need to use the SaveAs method on the document to change the file type and the name!
Private Sub InsVbaCode_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles InsVbaCode.Click
'Helper method to get current Word instance or, if none, start one
GetWordProcess()
Dim doc As Word.Document = WordApp.ActiveDocument
Dim vbModule As VBE.VBComponent = doc.VBProject.VBComponents.Add(VBE.vbext_ComponentType.vbext_ct_StdModule)
vbModule.Name = "basAddedCode"
vbModule.CodeModule.AddFromString( _
"Sub AutoOpen()" & vbLf & _
" MsgBox ""Document "" & ActiveDocument.FullName & "" has been opened successfully!""" & vbLf & _
"End Sub")
'doc.Save() or doc.SaveAs to change file type and/or name
End Sub
I have a workbook which is throwing this error on opening. When it does and I open the VBA module, the current line is the definition of a sub. But the only option is to kill the whole Excel process.
I've got custom document properties, I've got embedded combo-box controls, I have no clue what it might be, and Excel isn't helping.
However, when I open the same file on another computer - it doesn't throw the error.
Does anyone have any experience or advice with this kind of error?
Here's the Open code, but the 'Show Next Statement' command doesn't point here when the error occurs:
````
Private Sub Workbook_Open()
Dim ans
If Range("currentstatus") Like "*Ready for Year-End Preparation*" Then
ans = MsgBox("This workbook is ready for Year-End Preparation" & vbCrLf & "Would you like to begin?", vbYesNo)
If ans = vbYes Then
Range("Phase") = "Year-End"
SheetsSet 3
End If
End If
'Exit Sub
If Range("Phase") = "Commissions" Then
If Range("currentstatus") Like "*RVP/Dept Head Approved*" Then
ans = MsgBox("Commissions have been approved for " & Range("applicablemonth") & vbCrLf & "Would you like to enter data for the new period?", vbYesNo + vbQuestion)
If ans = vbYes Then
Range("ApplicableMonth") = Format(DateAdd("m", 1, CVDate(Range("applicablemonth"))), "YYYY-MM")
Range("CurrentStatus") = "Ready for Data Entry for " & Range("ApplicableMonth")
' now reset the summary page
Prot False, "Commission Form Summary"
Range("SalesPersonComplete") = Range("Summary")
Range("RVPComplete") = ""
Range("BrMgrComplete") = ""
Prot True, "Commission Form Summary"
Sheets("Menu").Select
' MsgBox "Begin."
End If
End If
End If
End Sub
I had this message earlier today and it was due to another instance of Excel being open as a background process (the background process had previously opened the file in question, so it must have been something to do with that). Once I closed the other instance the problem disappeared.
It might be worth checking 'Task Manager' > 'Background processes' to see if that's the case.
This sounds like a Voodoo procedure, but what helps when I got this error is to edit any of the VBA code (for example in some module add a linebreak and remove it) and then save the workbook. Maybe it's some kind of caching issue in my case but I thought it might help some of you too.
Double-check your file extension. Excel spreadsheets with macros embedded need a *.xlsm extension, not *.xls.
Total 'for-dummies' answer, but I just made this mistake myself.
The problem I have got is that my corporate template set uses a SaveDate field in the footer of every word document - which is used to detail when the document was saved, which ties in with our custom document management system.
Subsequently, when users want to make a PDF of an old document, using the Save As PDF function of Office 2010, the Save Date is updated - creating a PDF of the old document, but with today's date. This is wrong. We are just trying to create a true PDF version of whatever the original document has in it.
To get around this, I am writing a macro solution which locks the fields, exports the document as a PDF and then unlocks the fields again.
I have come up against an issue where I can identify and lock all fields in the headers/footers (which is actually what I'm trying to do) but to make it more robust, need to find out a way to lock ALL FIELDS in ALL SECTIONS.
Showing you my code below, how can I identify all fields in all sections? Will this have to be done using the Index facility?
Sub CPE_CustomPDFExport()
'20-02-2013
'The function of this script is to export a PDF of the active document WITHOUT updating the fields.
'This is to create a PDF of the document as it appears - to get around Microsoft Word 2010's native behaviour.
'Route errors to the correct label
'On Error GoTo errHandler
'This sub does the following:
' -1- Locks all fields in the specified ranges of the document.
' -2- Exports the document as a PDF with various arguments.
' -3- Unlocks all fields in the specified ranges again.
' -4- Opens up the PDF file to show the user that the PDF has been generated.
'Lock document fields
Call CPE_LockFields
'Export as PDF and open afterwards
Call CPE_ExportAsPDF
'Unlock document fields
Call CPE_UnlockFields
'errHandler:
' MsgBox "Error" & Str(Err) & ": " &
End Sub
Sub CPE_LockFields()
'Update MS Word status bar
Application.StatusBar = "Saving document as PDF. Please wait..."
'Update MS Word status bar
Application.StatusBar = "Locking fields in all section of the active document..."
'Declare a variable we can use to iterate through sections of the active document
Dim docSec As section
'Loop through all document sections and lock fields in the specified ranges
For Each docSec In ActiveDocument.Sections
docSec.Footers(wdHeaderFooterFirstPage).Range.fields.Locked = True
docSec.Footers(wdHeaderFooterPrimary).Range.fields.Locked = True
docSec.Footers(wdHeaderFooterEvenPages).Range.fields.Locked = True
Next
End Sub
Sub CPE_UnlockFields()
'Update MS Word status bar
Application.StatusBar = "PDF saved to DocMan Temp. Now unlocking fields in active document. Please wait..."
'Declare a variable we can use to iterate through sections of the active document
Dim docSec As section
'Loop through all document sections and unlock fields in the specified ranges
For Each docSec In ActiveDocument.Sections
docSec.Footers(wdHeaderFooterFirstPage).Range.fields.Locked = False
docSec.Footers(wdHeaderFooterPrimary).Range.fields.Locked = False
docSec.Footers(wdHeaderFooterEvenPages).Range.fields.Locked = False
Next
End Sub
Sub CPE_ExportAsPDF()
'Update MS Word status bar
Application.StatusBar = "Saving document as PDF. Please wait..."
'Chop up the filename so that we can remove the file extension (identified by everything right of the first dot)
Dim adFilename As String
adFilename = Left(ActiveDocument.FullName, (InStrRev(ActiveDocument.FullName, ".", -1, vbTextCompare) - 1)) & ".pdf"
'Export to PDF with various arguments (here we specify file name, opening after export and exporting with bookmarks)
With ActiveDocument
.ExportAsFixedFormat outPutFileName:=adFilename, _
ExportFormat:=wdExportFormatPDF, OpenAfterExport:=True, _
OptimizeFor:=wdExportOptimizeForPrint, Range:=wdExportAllDocument, _
Item:=wdExportDocumentContent, IncludeDocProps:=True, KeepIRM:=True, _
CreateBookmarks:=wdExportCreateWordBookmarks, DocStructureTags:=True, _
BitmapMissingFonts:=True, UseISO19005_1:=False
End With
'Update MS Word status bar
Application.StatusBar = "PDF saved to DocMan Temp."
End Sub
Try something like the following to get to all fields in the document, header, footer, background and main text:
Sub LockAllFieldsInDocument(poDoc As Document, Optional pbLock As Boolean = True)
Dim oRange As Range
If Not poDoc Is Nothing Then
For Each oRange In poDoc.StoryRanges
oRange.Fields.Locked = pbLock
Next
End If
Set oRange = Nothing
End Sub
Here is another way to do it. It'll select the entire document and then lock all fields, before deselecting everything.
Sub SelectUnlink()
ActiveDocument.Range(0, 0).Select
Selection.WholeStory
Selection.Range.Fields.Unlink
Selection.End = Selection.Start
End Sub
Background: I want to use a specific entered text from a TextBox for the default filename in the SaveAs dialog.
I have implemented the following VBA script in my document, a Word 2010 template .dotm
Sub FileSaveAs()
'for testing
Dim fileName As String
fileName = Me.tb_myTextBox.Value & "_MyFileNameToSave"
MsgBox fileName
'use specific file name in save dialog
With Dialogs(wdDialogFileSaveAs)
.Name = fileName
.Show
End With
End Sub
It works fine, when I run it. I saved the .dotm, closed it and reopened it out from the Windows Explorer (means as "end user").
BUT in this case, means after open the template document as "end user" (so that I can save a new doc out of it and not overwrite the template), the content/value of the TextBox is empty, even if I entered something into it.
So, how can I read out the data of the TextBox in "document mode" of a template?
Presumably, the OP's intention was something along the lines of:
Sub FileSaveAs()
Dim StrNm As String
With ActiveDocument
StrNm = Split(.Shapes(1).TextFrame.TextRange.Text, vbCr)(0) & "_MyFileNameToSave"
'use specific file name in save dialog
With Dialogs(wdDialogFileSaveAs)
.Name = StrNm
.Show
End With
End With
End Sub
where .Shapes(1) identifies the particular textbox Shape object.
how can I read out the data of the TextBox in "document mode" of a template?
Not sure what you mean. This works for me:
create a form:
Private Sub btn_OK_Click()
Dim fileName As String
fileName = tb_myTextBox.Value & "_MyFileNameToSave"
With Dialogs(wdDialogFileSaveAs)
.name = fileName
.Show
End With
End Sub
create a sub to call this form:
Sub FileSaveAs()
UserForm1.Show
End Sub
This is all saved in a template / .dotm.
Now, create a document off of the template (double click the template to launch document off of it). Alt + F8 and run the macro from the template (you may have to select the template from the "Macros in" drop down). Result: my form comes up, I enter a name for the document, press ok, and the Word Save As dialog appears with the name I gave to the document.