I'm trying to create word documents on the fly in VB.NET, and I've found that the documentation on all of this seems very scarce. Right now my program works by looping through a database table, and for each row it pulls out the variables. The program then loops through and parses a template word doc, replacing variables in that template with variables from the database, then saving as a new doc.
Instead, for every "new doc" that I would be creating, I want to just append it onto another doc, that way when it comes time to parse 1000 rows in the table, I don't have to create and save 1000 different word documents to the filesystem.
I can't seem to find anything out there about this. Everything mentions merging documents (I actually want to append, not merge) or the only way I can append ("Insert") a document is by using WordApplication.Selection.InsertFile(newWordDocument.FullName). This works, but requires me to save newWordDocument to the filesystem before inserting it.
Is there a way to, while still in memory, add newWordDocument to my WordApplication object?
Here's the pseudocode of what I have now
For each row in TableOfVariables
Dim WordApplication as New Word.Application
Dim tempDoc as New Word.Document
tempDoc = WordApplication.Documents.Add
tempDoc = fillVariablesOfTheDocument(tempDoc, row)
tempDoc.Save() 'This is the problem - it saves as a new file rather than appending into WordApplication
Next
You didn't make an assignment, just a statement with
objWordApp.Documents.Add(tempDoc) 'This is where the problem is!
Dim wordApp as new Microsoft.Office.Interop.Word.Application
wordApp.Visible = true
Dim doc as Word.Document
doc = wordApp.Documents.Add
and you don't have to save it until you are good and ready. This also works
Dim Word As Word.Application
Dim Doc As Word.Document
'Start Word and open the document template.
Word = CreateObject("Word.Application")
Word.Visible = True
Doc = Word.Documents.Add
from http://support.microsoft.com/kb/316383
Took a while, but somehow I found out about MailMerge, which I had no idea existed. Long story short, I can have a template document with variables, and I can have an input excel document. The excel document will have a header row which contains the variable names, and each individual cell of the excel doc will represent the variable that will go into the document. When I execute MailMerge, the template will be replaced with n amount of pages (where n is the number of excel rows). This solves my problem of adding multiple documents to 1 big excel document. I tested this with 1,000 rows in my excel doc, and it worked flawlessly.
Here's the code I ended up with:
Dim wrdSelection As Word.Selection
Dim wrdMailMerge As Word.MailMerge
Dim wrdMergeFields As Word.MailMergeFields
' Create an instance of Word and make it visible.
wrdAppMailMrg = CreateObject("Word.Application")
wrdAppMailMrg.Visible = True
' Add a new document.
wrdDocMailMrg = wrdAppMailMrg.Documents.Open("Template.docx")
wrdDocMailMrg.Select()
wrdSelection = wrdAppMailMrg.Selection()
wrdMailMerge = wrdDocMailMrg.MailMerge()
'Open Data Source
wrdDocMailMrg.MailMerge.OpenDataSource("InputVariables.xlsx", SQLStatement:="SELECT * FROM [Sheet1$]")
' Perform mail merge.
wrdMailMerge.Destination = _
Word.WdMailMergeDestination.wdSendToNewDocument
wrdMailMerge.Execute(False)
' Close the original form document.
wrdDocMailMrg.Saved = True
wrdDocMailMrg.Close(False)
' Release References.
wrdSelection = Nothing
wrdMailMerge = Nothing
wrdMergeFields = Nothing
wrdDocMailMrg = Nothing
wrdAppMailMrg = Nothing
Now I just have to tweak it to make it run faster and clean up the UI, but the functionality is completely there.
Related
I am trying to read one pdf and a VBA userform and then fill out another pdf.
I wrote code to read all text in a pdf and then find certain sub strings based on tokens that I can find in the string. It is intended to populate the fields in the destination pdf based on the substrings and check the appropriate text boxes based on the user form. I can get the code to fill the substrings and then save the document, but it won't check the boxes.
Before the code used a AVDoc, but I switched to a JSO because I don't want the pdf to pop up, and the jso avoids that problem.
I tried pdfBool.value = cBool(vbaBool), pdfBool.value = 1, pdfBool.value = "1", jso.setValue("checked"), jso.setValue("yes"), etc.
This code will run without crashing. I reduced the number of variables to one string and one bool for the sake of the example.
Sub main()
‘findString grabs all text from a pdf file. This code works.
Dim mystr As String
If findString(mystr) = False Then
Application.StatusBar = "Cannot find Source PDF"
Exit Sub
End If
Dim mypath As String
mypath = ActiveWorkbook.Path & "\destination.pdf"
Dim aApp As acrobat.AcroApp
Dim pdfDoc As acrobat.CAcroPDDoc
Dim jso As Object
Set aApp = CreateObject("AcroExch.App")
Set pdfDoc = CreateObject("AcroExch.PDDoc")
If pdfDoc.Open(mypath) = True Then
Set jso = pdfDoc.GetJSObject
Dim vbaText As String
Dim vbaBool As String
vbaText = returnString("Token1")
vbaBool = userForm.checkBox1.value
Dim pdfText As Object
Dim pdfBool As Object
Set pdfText = jso.getField("TextField1")
Set pdfBool = jso.getField("CheckBox1")
pdfText.Value = vbaText
pdfBool.Value = vbaBool
'save pdffile
Dim fileSavePath As String
fileSavePath = ActiveWorkbook.Path & "\My Save File.pdf"
pdfDoc.Save PDSaveFull, fileSavePath
'clean up memory
Set pdfDoc = Nothing
Set pdfText = Nothing
Set pdfBool = Nothing
Set jso = Nothing
End If
aApp.Exit
Set aApp = Nothing
Unload userForm1
End Sub
Ok, so after some searching, I have found a solution. Basically, forms created using Living Cycle don't work well with checkboxes. I asked somebody in my organization and they confirmed that Living Cycle was used on forms for a while until we got rid of it. Honestly, I don't know what Living Cycle is, but the solution seemed to work and so I think whatever the issue was related to something called "Living Cycle".
The solution? Redo the pdf form: I exported the pdf to an Encapsulated PostScript file. This stripped away all the fields. After that, I used the prepare form tool which automatically found all the relevant fields. Fortunately, with my pdf, it found all of the fields perfectly, though there was one or two extra ones that I had to delete. The field names and the code need to match so adjustments need to either be made to the PDF or to the code, but once I made that adjustment, everything was perfect.
Try jso.getfield(pdfFieldName).Value = "Yes" or "No". The value is case sensitive so you have to use Yes or No.
I am trying to use excel data to perform various tasks on a word document. However, this will be used by other people, so I wanted to copy all contents from the word doc (and formatting and tables) to a new word doc and edit the new one to prevent others from accidentally saving over the first one (template one). I also want to close the template (first) doc and keep the second (new) one open.
I am new to VBA and am having trouble understanding how to handle this. After a bunch of googling, I've compiled this (not working) code:
Dim WordApp As Object
Dim WordDoc As Word.Document
Dim NewWordDoc As Word.Document
'Dim other stuff'
'-------------------------------------------------------------------
Set WordApp = CreateObject("Word.Application")
WordApp.Visible = True
Set WordDoc = WordApp.Documents.Open("C:\Users\Windows\Documents\CRC Labels
test.doc")
Set NewWordDoc = WordApp.Documents.Add 'I think this gives an error'
'------------------------------------------------------------------------
'Line that actives WordDoc'
Selection.WholeStory 'Select whole document
Selection.Expand wdParagraph 'Expands your selection to current paragraph
Selection.Copy 'Copy your selection
'Line that closes WordDoc'
'Line that activates NewWordDoc'
Selection.PasteAndFormat wdPasteDefault 'Pastes in the content
'------------------------------------------------------------------------
'Do stuff with NewWordDoc'
'------------------------------------------------------------------------
Set WordDoc = Nothing
Set WordApp = Nothing
Any other ideas of handling this situation? Thank you for your response.
When you find yourself using an existing document as the basis for creating new documents it's time to consider saving that document as a Word Template. This is a particular type of file (*.dotx or *.dotm) that may contain
boiler-plate text
Building blocks with additional boiler-plate to be inserted as required
styles (formatting command sets)
customized keyboard shortcuts
Ribbon customizations
VBA code (macros, in *.dotm, only)
that will be inherited and/or shared by all documents generated from the template.
In order to create a new document from a template, use the Documents.Add method, specifying the path and file name:
Dim wdDoc as Word.Document
Set wdDoc = Documents.Add(path & filename)
'From outside Word: Set wdDoc = wdApplication.Documents.Add
This will create a new document from the template, copying the complete template content without affecting the template, itself.
Note: You can also use a "plain document" (*.docx) with the Documents.Add method and it will copy the content. It will not, however, link back to that document so it can't share macros, for instance.
Background
I have a Word-macro in Normal.dotm that opens an Excel-workbook Reference Check.xlsm.
The macro uses RegExp as well as Range.Find to find strings of a certain pattern, and then it places this information onto sheets in the Excel-workbook.
This is the basic template:
Sub GetReferencesAndHyperlinksToExcel()
' Shortcut Key: Ctrl+[Num *]
Dim oRegex As New RegExp
Dim oRegExMatches As MatchCollection
Dim oRegExMatch As Match
Const strPattern As String = "(my pattern)"
Dim SourceDocument As Document
Dim oStory As Range
Dim hl As Hyperlink
Dim xlApp As New Excel.Application
Dim xlWorkbook As Excel.Workbook
Dim R as Excel.Range
' Set the context
Set SourceDocument = ActiveDocument
' Load the spreadsheet
Set xlWorkbook = xlApp.Workbooks.Open("C:\...\Reference Check.xlsm")
' Set starting range to write to
With xlWorkbook.Worksheets("Reference Checks")
.Range("ref").ClearContents
Set R = .Range("ref")(1, 1)
End With
' Initialise regex
With oRegex
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = strPattern
End With
' ==== Iterate through story ranges, then execute regex and write matches and hyperlinks to the spreadsheet ====
' ==== Iterate through story ranges, then iterate through hyperlinks and write hyperlinks to the spreadsheet ====
End Sub
The Excel-workbook does some calculations and lookups (with pre-filled column formulas) against a Data Table within the Workbook.
Using that information, I manipulate the active Word document (the "caller" or the "context") in predictable^ ways.
^If it's predictable, then we should be able to use a macro to do the work for us.
The problem
I want to use the now-open Excel workbook Reference Checks.xlsm as a "console" or "controller", where I can change some data, and use data, to perform operations on (make changes to) the document.
To do this, I believe I need to somehow be able to refer to the original "caller" or "context", i.e. the Word document that was Active when I originally called it (Set SourceDocument = ActiveDocument), from within the now-open Workbook. This means that either:
Macros within Excel (tied to Buttons) Reference Check.xlsx act on SourceDocument (preferred), or
Macros within Excel (tied to Buttons) Reference Check.xlsx call a Macro within Word Normal.dotm and then act on the SourceDocument* (if this is possible, the above should be), or
Various macros within Normal.dotm act on SourceDocument* (workable, but clunky)
*Note: This must actually be SourceDocument (the "caller" or "context") and not ActiveDocument, because it is possible that as I switch between windows, another document could become ActiveDocument.
Things I've thought of
For option 1, use a global variable in Reference Checks.xlsm, then when I load the workbook, set the reference to SourceDocument. This is my preferred option. I have no idea how to do this, however.
For option 3, use a global variable in Normal.dotm, set the reference to the workbook when I open it, then read information in the workbook when I call the macros. A drawback would be that I have many macros I might call, and I would have to have many public subs (makes Normal.dotm quite unwieldy).
Help
How do I do this?
I have tested this and it worked perfectly with Office 2010.
Note that you need the Excel Library in your Word Normal Template and the Word Library in the Excel Reference Check.xlsm
You will need the following:
Sub in the ThisWorkBook module in the Reference Check.xlsm.
So in your Excel VBA Project -> Microsoft Excel Objects -> ThisWorkBook
Option Explicit
Sub ExcelWordController(WordDocFromWord As Document)
'If you are wanting to use it as a Document then _
you will need the Word Library in Excel
MsgBox WordDocFromWord
'Just put some funny text inthe doc for a test
WordDocFromWord.Range(0, 0).Text = "Yeah Baby It works"
'Here you can then call someother Sub or so some processing
End Sub
Then in your Word Normal project:
Option Explicit
'Sub in Word Normal
Sub ExcelControlWordFromWordInitiation()
'Obvisouly you alrady have the Excel Library in the Word Doc
Dim xlApp As New Excel.Application
'xlWorkbook is a object in the Word Excel Library
'So I changed the name
Dim RefCheckWorkBook As Excel.Workbook
Dim R As Excel.Range
Dim SourceDocument As Document
Set SourceDocument = ActiveDocument
'You want to be able to see the Excel Application
xlApp.Visible = True
Set RefCheckWorkBook = xlApp.Workbooks.Open(Environ("USERPROFILE") & "\Desktop\Reference Check.xlsm")
'Calling the Sub in Excel from Word
Excel.Application.Run "ThisWorkBook.ExcelWordController", SourceDocument
End Sub
I am using send-keys to select all data and copy it from another application. My goal is to paste this data into word and save as a PDF. The problem i seem to have, is that using Microsoft interop, requires you to programattically format the data. If i copy the data from the other application, and paste it manually in a real word doc, the format retains itself.
Is there a way to easily take my clipboard data and use it with this code?
Try
Dim oWord As Word.Application
Dim oDoc As Word.Document
'Start Word and open the document template.
oWord = CreateObject("Word.Application")
oWord.Visible = True
oDoc = oWord.Documents.Add
oPara1 = oDoc.Content.Paragraphs.Add
oPara1.Range.Text = Clipboard.SetText
'TIll Above your entire odoc is formatted
'From below I will save it to my own code
Dim newdoc As Word.Document
newdoc = oDoc
newdoc.SaveAs2("K:\file.pdf", Word.WdSaveFormat.wdFormatPDF)
'All done. Close this form.
'BSPGlobals.DataBase.Contact.ExitApp()
MessageBox.Show("Print to Doc Done.")
Catch ex As Exception
MessageBox.Show("Error at Printing the bill." & vbCrLf & ex.Message)
End Try
Depending on the source and format of the Clipboard data, you can influence the way the Clipboard contents is pasted into Word by fiddling around with the following Application options (don't forget to restore the original settings when you are done):
' when pasting between different Office documents
oWord.Options.PasteFormatBetweenDocuments = Word.WdPasteOptions.wdKeepSourceFormatting
' when contents is copied from a document that uses styles
oWord.Options.PasteFormatBetweenStyledDocuments = Word.WdPasteOptions.wdKeepSourceFormatting
' when pasting from an external source such as a web page
oWord.Options.PasteFormatFromExternalSource = Word.WdPasteOptions.wdKeepSourceFormatting
Thanks in advance for any help you can give me.
I'm trying to create a VB application that will open an existing Word document, make some changes and save it with a new file name. Making the changes to the document is easy. Saving the document seems like it should be just as easy but there is one serious problem. When I try to save the document, the save as dialog opens. This is supposed to be automated so that doesn't work. I have tried a whole bunch of variations of:
Sub Main()
Dim oWord As Word.Application
Dim oDoc As Word.Document
Dim fileName As String
Dim templateName As String
Dim newFileName As String
'Start Word and open the document template.
oWord = CreateObject("Word.Application")
oWord.Visible = False
oWord.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone
templateName = "C:\tmp\template.dotx"
fileName = "C:\tmp\document.docx"
newFileName = "C:\tmp\new document.docx"
oDoc = oWord.Documents.Add(templateName)
'oDoc = oWord.Documents.Open(fileName) I have tried both using a template and opening a docx file.
<make changes>
oDoc.SaveAs2(newFileName)
oWord.Documents.Close(Word.WdSaveOptions.wdDoNotSaveChanges)
oWord.Application.Quit()
oWord = Nothing
End Sub
It always stops here:
oDoc.SaveAs2
and opens the save as dialog. I need to find a way to suppress the save as dialog or a new way of editing the word files. Everything I have found so far about the problem has either not been solved/updated or was related to Word addins. I don't have any of the addins that people said caused the problem. To be safe, I disabled all word addins.
I would appreciate it if anyone has either solved it or has a different approach. I'm not stuck on the idea of using VB. The only reason I'm going this route is because I think it gives me the best library for editing charts and formatting the documents.
Thanks, Steve
I found the problem. The answer lies in what I was doing in the part I omitted where I was making changes to the document. Part of those changes are to update data in the charts in the document. I had something like:
For Each oShape As Word.InlineShape In oDoc.InlineShapes
If oShape.HasChart And oShape.Range.Bookmarks.Item(1).Name = "ChartName" Then
Console.WriteLine("Updating ChartName")
Dim oWorkbook As Excel.Workbook
oWorkbook = oShape.Chart.ChartData.Workbook
oWorkbook.Worksheets(1).Range("B2").FormulaR1C1 = "9"
oWorkbook.Worksheets(1).Range("B3").FormulaR1C1 = "5"
oWorkbook.Worksheets(1).Range("B4").FormulaR1C1 = "1"
oWorkbook.Application.Quit()
End If
Next
I changed it to:
For Each oShape As Word.InlineShape In oDoc.InlineShapes
If oShape.HasChart And oShape.Range.Bookmarks.Item(1).Name = "ChartName" Then
Console.WriteLine("Updating ChartName")
Dim oWorkbook As Excel.Workbook
oWorkbook = oShape.Chart.ChartData.Workbook
oWorkbook.Worksheets(1).Range("B2").FormulaR1C1 = "9"
oWorkbook.Worksheets(1).Range("B3").FormulaR1C1 = "5"
oWorkbook.Worksheets(1).Range("B4").FormulaR1C1 = "1"
oShape.Chart.Refresh()
oWorkbook.Close(True)
End If
Next
Now it works
myDoc.SaveAs(fileNameAndPath)
Works for me in Word 2007