PDA

View Full Version : Use Excel To Open Word



jo15765
10-27-2011, 10:07 AM
I have several employees and each employee has a table in a access 2000 database with a primary key defined. What I am wanting is to be able to build a form in excel, that has two boxes, one for the Employee name, and the other for the ID. Then a press button, and once this button is pressed it will open the word doc that is in let's say EmployeeName field on the form, and run a mail merge with the ID that is in the IDRequested field on the form.

I have all of these steps set up manually, ie the access database is built, the word doc with mail merge set up etc. I just would like to try to automate it and make it as simple as enter the employee name, enter the ID requested an Bam, there is what you are looking for.

I have this code that will open word for me, I just don't know how to pass the two parameters....

Dim wdApp As Word.Application, wdDoc As Word.Document

On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
If Err.Number <> 0 Then 'Word isn't already running
Set wdApp = CreateObject("Word.Application")
End If
On Error GoTo 0

Set wdDoc = wdApp.Documents.Open("C:\temp\anyolddoc.doc")

wdApp.Visible = True

End Sub

Kenneth Hobs
10-27-2011, 11:21 AM
Doing it manually is the first step. In fact, you are almost done. Of course this could all be done in MSWord but doing it in Excel is what we are here for. After you complete your project, please post the code here if you don't mind. This helps others by having real world examples.

Are the ID's not in the same record as the Names? I can see a case where you might have two John Does but different IDs. Your SQL should limit records to ID most likely.

Record a macro in MSWord where you do the merge. Then adapt that code for Excel. To get the ID and/or Names, you can do that in listbox controls on the Sheet or a Userform with listbox controls by use of ADO methods.

See this link for an example doing mail merge to MSWord from Excel. The key is to pass the correct SQL string. http://www.excelforum.com/excel-programming/796614-mail-merge-from-excel.html

For ADO methods, see: http://www.erlandsendata.no/english/index.php?t=envbadac and Ron's examples, http://www.rondebruin.nl/tips.htm.

In this thread, http://www.vbaexpress.com/forum/showthread.php?p=167033, I showed how to use ADO to fill a listbox control in a userform from an MDB file. Similar to that, I did it in ActiveX listbox controls. I attached a spreadsheet to show both listbox control methods. To test it, you will need the readily available NWind.mdb file as explained in the thread and replace the paths in the code routines.

For more information and examples see:
'Word 's Catalogue/Directory Mailmerge facility (the terminology depends on the Word version). _
To see how to group records with any mailmerge data source supported by Word, _
check out my Microsoft Word Catalogue/Directory Mailmerge Tutorial at:
' http://lounge.windowssecrets.com/index.php?showtopic=731107
' or
' http://www.gmayor.com/Zips/Catalogue%20Mailmerge.zip

jo15765
10-27-2011, 11:34 AM
I am not sure how to get a macro in word to record what I am doing. It is set up currently that when you open the worddoc, let's take Mark for example when I double click on Mark.doc it opens, and then it opens the access database and automatically asks me for the id I want to view in word. How could I record a macro that catches those steps?

Kenneth Hobs
10-27-2011, 11:42 AM
It has been awhile since I did that. Why not start the recording in MSWord and then do a File > Open rather an open via Explorer or other method?

jo15765
10-27-2011, 11:50 AM
I tried opening word and recording a macro to catch what goes on "behind the scenes" but the macro record looses it becuase once the ID is entered it opens a new instance of word, and that "New" instance is not the active.document :(

Because opening the word document only opens the template, I then still have to enter the ID that I wish to "merge". I was able to capture the code through a macro that actually "merges" the template with the ID Document and that is:

With ActiveDocument.MailMerge
.Destination = wdSendToNewDocument
.MailAsAttachment = False
.MailAddressFieldName = ""
.MailSubject = ""
.SuppressBlankLines = True
With .DataSource
.FirstRecord = wdDefaultFirstRecord
.LastRecord = wdDefaultLastRecord
End With
.Execute Pause:=True
End With

Kenneth Hobs
10-27-2011, 01:43 PM
You need to get the string for the SQLStatement. Record a simple merge to get what you need. e.g. Open a new file, add one field, do the merge.

I used a txt file for this merge example as a standard routine in MSWord's VBA. The FRM file is the DOC file of course. Notice how I suppressed the initial merge in the Documents.Open. I initially did this project in WordPerfect for a consultant fee. They then asked for an MSWord solution and I gave it to them but they never paid for that. Oh well, so much for consulting.

Once you have the SQL string, that can easily be added to this routine.

Sub MergeRun(frmFile As String, datFile As String, _
Optional bClose As Boolean = True, Optional bPrint As Boolean = True, _
Optional iNoCopies As Integer = 1)
If Dir(frmFile) = "" Or Dir(datFile) = "" Then Exit Sub

'On Error GoTo endnow
Application.DisplayAlerts = wdAlertsNone

'Open form file and associate data file
Documents.Open frmFile, False, True, False
ActiveDocument.MailMerge.MainDocumentType = wdFormLetters
ActiveDocument.MailMerge.OpenDataSource name:=datFile, _
ConfirmConversions:=False, ReadOnly:=False, LinkToSource:=True, _
AddToRecentFiles:=False, PasswordDocument:="", PasswordTemplate:="", _
WritePasswordDocument:="", WritePasswordTemplate:="", Revert:=False, _
Format:=wdOpenFormatAuto, Connection:="", SQLStatement:="", SQLStatement1 _
:="", SubType:=wdMergeSubTypeOther
'Merge to a new document
With ActiveDocument.MailMerge
.Destination = wdSendToNewDocument
.SuppressBlankLines = True
With .DataSource
.FirstRecord = wdDefaultFirstRecord
.LastRecord = wdDefaultLastRecord
End With
.Execute Pause:=False
End With

If bPrint = True Then
Application.PrintOut FileName:="", Range:=wdPrintAllDocument, Item:= _
wdPrintDocumentContent, Copies:=iNoCopies, Pages:="", PageType:=wdPrintAllPages, _
ManualDuplexPrint:=False, Collate:=True, Background:=True, PrintToFile:= _
False, PrintZoomColumn:=0, PrintZoomRow:=0, PrintZoomPaperWidth:=0, _
PrintZoomPaperHeight:=0
End If

If bClose = True Then
ActiveDocument.Close False
ActiveDocument.Close False
End If
endnow:
Application.DisplayAlerts = wdAlertsAll
End Sub



Of course this routine needs tweaked to add the MSWord references to use it as a standard Excel routine.

jo15765
10-27-2011, 01:48 PM
And the SQL string is just simply the SQL from the query that the mail merge calls, correct?

Here is a snippet of the SQL for my access query..And ID is the prompt that displays as soon as you open Richard.doc so that once you enter the ID it pulls in all of the data from the query.

SELECT tbl_Richard.ID, tbl_Richard.FName, tbl_Richard.lName, tbl_Richard.phone...
FROM tbl_Richard
WHERE (((tbl_Richard.ID)=[ID]));

Kenneth Hobs
10-27-2011, 02:05 PM
Right, it should be fairly similar. What you will do is to rebuild the string that will replace the [ID] with the value from the selection list in Excel for that ID that matches the Name that you need.

I did that one project several years ago so I would have to make up a new example to see the exact syntax.

jo15765
10-27-2011, 02:09 PM
I am wanting the ID to be entered into a text box on form in Excel. So I would have to say something like ID = Textbox1.Value or something similar to that, correct?

Kenneth Hobs
10-27-2011, 02:15 PM
Right. Try a hard coded string first but it may go something like:
s = "SELECT tbl_Richard.ID, tbl_Richard.FName, tbl_Richard.lName, tbl_Richard.phone FROM tbl_Richard WHERE (((tbl_Richard.ID)=" & "'" & Textbox1.value & "')))" more or less. Note the single quotes around the value.

If I get time, I will see if I can make up a quick example using nwind.mdb in an MSWord file using an SQL string in my MergeRun routine. Note how I set the path to the datFile in MergeRun which in this case would be the MDB file.

Kenneth Hobs
10-27-2011, 05:17 PM
Here is my simple recorded MSWord VBA mailmerge code. In particular, note this syntax:
ActiveDocument.MailMerge.DataSource.QueryString = _
"SELECT * FROM `Employees` WHERE `EmployeeID` = 3" & ""

Sub Macro4()
'
' Macro4 Macro
'
'
ActiveDocument.MailMerge.MainDocumentType = wdFormLetters
ActiveDocument.MailMerge.EditMainDocument
ActiveDocument.MailMerge.OpenDataSource Name:= _
"C:\MyFiles\Access\Nwind.mdb", ConfirmConversions:=False, ReadOnly:=False _
, LinkToSource:=True, AddToRecentFiles:=False, PasswordDocument:="", _
PasswordTemplate:="", WritePasswordDocument:="", WritePasswordTemplate:= _
"", Revert:=False, Format:=wdOpenFormatAuto, Connection:= _
"Provider=Microsoft.ACE.OLEDB.12.0;User ID=Admin;Data Source=C:\MyFiles\Access\Nwind.mdb;Mode=Read;Extended Properties="""";Jet OLEDB:System database="""";Jet OLEDB:Registry Path="""";Jet OLEDB:Engine Type=4;Jet OLEDB:Database Locking Mode=0;Jet OLEDB:Global Par" _
, SQLStatement:="SELECT * FROM `Employees`", SQLStatement1:="", SubType:= _
wdMergeSubTypeAccess
ActiveDocument.MailMerge.EditMainDocument
ActiveDocument.MailMerge.Fields.Add Range:=Selection.Range, Name:= _
"EmployeeID"
Selection.TypeParagraph
ActiveDocument.MailMerge.Fields.Add Range:=Selection.Range, Name:= _
"LastName"
Selection.TypeParagraph
ActiveDocument.MailMerge.Fields.Add Range:=Selection.Range, Name:= _
"FirstName"
ChangeFileOpenDirectory "X:\MSWord\MailMerge\KenExample\"
ActiveDocument.SaveAs2 FileName:="KenExample.docx", FileFormat:= _
wdFormatXMLDocument, LockComments:=False, Password:="", AddToRecentFiles _
:=True, WritePassword:="", ReadOnlyRecommended:=False, EmbedTrueTypeFonts _
:=False, SaveNativePictureFormat:=False, SaveFormsData:=False, _
SaveAsAOCELetter:=False, CompatibilityMode:=14
ActiveDocument.MailMerge.DataSource.QueryString = _
"SELECT * FROM `Employees` WHERE `EmployeeID` = 3" & ""
ActiveDocument.Save
ActiveDocument.MailMerge.DataSource.QueryString = _
"SELECT * FROM `Employees` WHERE `EmployeeID` = 3" & ""
With ActiveDocument.MailMerge
.Destination = wdSendToNewDocument
.MailAsAttachment = False
.MailAddressFieldName = ""
.MailSubject = ""
.SuppressBlankLines = True
With .DataSource
.FirstRecord = wdDefaultFirstRecord
.LastRecord = wdDefaultLastRecord
End With
.Execute Pause:=True
End With
ActiveDocument.SaveAs2 FileName:="1.docx", FileFormat:=wdFormatXMLDocument _
, LockComments:=False, Password:="", AddToRecentFiles:=True, _
WritePassword:="", ReadOnlyRecommended:=False, EmbedTrueTypeFonts:=False, _
SaveNativePictureFormat:=False, SaveFormsData:=False, SaveAsAOCELetter:= _
False, CompatibilityMode:=14
ActiveWindow.Close
ActiveWindow.Close
Application.Quit
End Sub

Kenneth Hobs
10-27-2011, 08:04 PM
I put it all together for you in the XLSM file with an example DOCX. I used ADO to fill a combobox with the LastName, FirstName, EmployeeID from the Employees table in the Northwind database. I parsed the ID name from the string value in the combobox to build the SQL string. You will need to get nwind.mdb and change the paths accordingly. There are a few more routines in it than you really need. Look at the Sheet1 code which contains the code for the two command buttons.

Here is one place to get mwind.mdb. http://www.microsoft.com/download/en/details.aspx?id=19704

Kenneth Hobs
10-28-2011, 05:52 AM
I updated the MergeRunMDB routine to:
'Add Tools > References... > Microsoft Word Word 14.0 Object Libraray
Sub MergeRunMDB(frmFile As String, datFile As String, _
sSQL As String, _
Optional bClose As Boolean = False, Optional bPrint As Boolean = False, _
Optional iNoCopies As Integer = 1)

Dim wd As Word.Application, doc As Word.Document

If Dir(frmFile) = "" Or Dir(datFile) = "" Then Exit Sub

On Error Resume Next
Set wd = GetObject(, "Word.Application")
If wd Is Nothing Then
Set wd = CreateObject("Word.Application")
End If
wd.Visible = False
On Error GoTo 0
wd.Application.DisplayAlerts = wdAlertsNone

'Open form file and associate data file
Set doc = wd.Documents.Open(frmFile, False, True, False)
With doc
.MailMerge.MainDocumentType = wdFormLetters
.MailMerge.OpenDataSource Name:= _
datFile, ConfirmConversions:=False, ReadOnly:=False _
, LinkToSource:=True, AddToRecentFiles:=False, PasswordDocument:="", _
PasswordTemplate:="", WritePasswordDocument:="", WritePasswordTemplate:= _
"", Revert:=False, Format:=wdOpenFormatAuto, Connection:= _
"Provider=Microsoft.ACE.OLEDB.12.0;User ID=Admin;Data Source=datfile;Mode=Read;Extended Properties="""";Jet OLEDB:System database="""";Jet OLEDB:Registry Path="""";Jet OLEDB:Engine Type=4;Jet OLEDB:Database Locking Mode=0;Jet OLEDB:Global Par" _
, SQLStatement:=sSQL, SQLStatement1:="", SubType:= _
wdMergeSubTypeAccess

'sSQL = "SELECT * FROM `Employees`"
'Merge to a new document
With .MailMerge
.Destination = wdSendToNewDocument
.SuppressBlankLines = True
With .DataSource
.FirstRecord = wdDefaultFirstRecord
.LastRecord = wdDefaultLastRecord
End With
.Execute Pause:=False
End With

If bPrint = True Then
wd.ActiveDocument.PrintOut Range:=wdPrintAllDocument, Item:= _
wdPrintDocumentContent, Copies:=iNoCopies, Pages:="", PageType:=wdPrintAllPages, _
ManualDuplexPrint:=False, Collate:=True, Background:=True, PrintToFile:= _
False, PrintZoomColumn:=0, PrintZoomRow:=0, PrintZoomPaperWidth:=0, _
PrintZoomPaperHeight:=0
End If

.Application.DisplayAlerts = wdAlertsAll
If Not bClose Then wd.Visible = True
.Close False
If bClose = True Then
wd.ActiveDocument.Close False
wd.Quit
Set doc = Nothing
Set wd = Nothing
End If
End With
End Sub

jo15765
10-28-2011, 05:52 AM
This is what I want my form to look like, and I know I need to add in the directory where myd atabase is stored, as well as my SQL string...My question tho, is it is possible to pass data that is entered on this form into the query, and essentially into the mail merge?

Kenneth Hobs
10-28-2011, 06:03 AM
It should work ok after the conversion and replacement of MergeRunMDB and edits to the paths for the MDB and DOCX or DOC files. You will just need to reference the lower versions of the early bound objects. In VBE for the Excel file, go to menus Tools > References... and uncheck the Missing ones and replace with the next lower version.

snb at ExcelForum does more concise code. http://www.excelforum.com/excel-programming/798299-print-mail-merge-document.html

jo15765
10-28-2011, 07:08 AM
One possible snag that I see is that the SQL statement that is run, is dependent upon whose template is chosen (see attachment above). The SQL varies slightly for each different template as they each have there own query. Could the code say something like TemplateOpen = TextBox4.Value.

Then have the code for the database to open be C:\Test\TemplateOpen\TemplateOpen.mdb

Then I could input the actual query Name from within the TemplateOpen database

Kenneth Hobs
10-28-2011, 07:14 AM
The parameter inputs for MergeRunMDB() can be anything you want. That is why I do modular code. If you are going to use a query string already in the database, you will have to submit that SQL string to MergeRunMDB adapted to MSWord's syntax. It should be similar but it will be different.

You can use Access's existing SQL using Excel's QueryTables method to import that data to Excel. One could then use the Excel data as the data source but that involves another type of MergeRun routine.

Here is how to import the data to Excel using an existing Access stored SQL with some thread links for more info. Obviously, you can use the same nwind.mdb file. Just replace the path and name of the MDB file accordingly.
'http://www.vbaexpress.com/forum/showthread.php?t=24118
'http://www.vbaexpress.com/forum/showthread.php?t=24575
'http://www.vbaexpress.com/forum/showthread.php?t=23783
'http://www.vbaexpress.com/forum/showthread.php?t=26145
Sub Test()
Dim mdbPath As String, dbName As String, cmdText As String
Dim rngDestination As String
'mdbPath = "E:\ADO\NWind2003.mdb" 'change the path here to suit your needs
'mdbPath = "c:\myfiles\edrive\excel\ado\NWind2003.mdb"
mdbPath = "//matpc10/ExcelVBAExamples/ado/NWind2003.mdb"
dbName = "NWind2003_1" 'change the database name here to suit your needs
cmdText = "Aug94" 'change the stored SQL here to suit your needs
rngDestination = "A1" 'change the destination range here to suit your needs

'Clear previous data
Cells.Delete

InsertTableWithStoredSQL mdbPath, dbName, cmdText, rngDestination

'Insert other data to the right of A1 with a blank column separating the two
rngDestination = Cells(1, Columns.Count).End(xlToLeft).Offset(0, 2).Address
cmdText = "Sales by Category"
InsertTableWithStoredSQL mdbPath, dbName, cmdText, rngDestination
End Sub

Sub InsertTableWithStoredSQL(mdbPath As String, dbName As String, _
cmdText As String, rngDestination As String, _
Optional bFieldNames = True)

With ActiveSheet.QueryTables.Add(Connection:=Array( _
"OLEDB;Provider=Microsoft.Jet.OLEDB.4.0;Password="""";User ID=Admin;Data Source=" & mdbPath & ";Mode=ReadWrite;Extended Properties=""" _
, """;Jet OLEDB:System database="""";Jet OLEDB:Registry Path="""";Jet OLEDB:Database Password="""";Jet OLEDB:Engine Type=5;Jet OLEDB:Datab" _
, "ase Locking Mode=1;Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Global Bulk Transactions=1;Jet OLEDB:New Database Password="""";J" _
, "et OLEDB:Create System Database=False;Jet OLEDB:Encrypt Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Co" _
, "mpact Without Replica Repair=False;Jet OLEDB:SFP=False"), Destination:=Range("" & rngDestination & ""))
.CommandType = xlCmdTable
.CommandText = Array(cmdText)
.Name = dbName
.FieldNames = bFieldNames
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = False
.BackgroundQuery = True
.RefreshStyle = xlInsertDeleteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = True
.RefreshPeriod = 0
.PreserveColumnInfo = True
.SourceDataFile = mdbPath
.Refresh BackgroundQuery:=False
End With
End Sub

jo15765
10-28-2011, 07:21 AM
Oh Boy, this is far more advanced than I thought it was going to be!

Kenneth Hobs
10-28-2011, 07:41 AM
Regarding your Excel file, I recommend that you make the two top textboxes as Read Only or as Label controls.

If you want to just let the user enter what they think the userid or name might be, that is fine. Your SQL string can be constructed according to their userid first and if blank, then use the name. Obviously, your users must enter the correct information. This is why I used ADO to help the user pick the correct data to construct the SQL string. IF you let the user enter the values then you may want to add some code to my routine to exit if the recordcount is 0.

The stored SQL method may not be what you were talking about. I thought that you now want to use a stored SQL. If that is not the case, it is not all that complicated now that you have my latest MergeRunMDB routine.

jo15765
10-28-2011, 07:45 AM
Maybe my best bet isn't to try to go from Access to Excel to Word. I think I am going to look into going directly from Access to Word by using C# coding instead of my beginner knowledge of VBA.

Or I may end up with a dead end there, and find out that you have to do the same thing with C#

Kenneth Hobs
10-28-2011, 07:51 AM
That is your choice of course.

If you want to use VBA, your userform dialog could be used in Access as-well-as my MergeRunMDB.

jo15765
10-28-2011, 08:19 AM
What do you mean my userform dialog could be used in access as well as your MergeRunMDB?

Kenneth Hobs
10-28-2011, 10:40 AM
Access, like MSWord and Excel has VBA. As such, you can create, import, and export Modules where my MergeRunMDB would reside and userforms where your dialog resides. It is easy. In VBE, right click the object in the Project Explorer, View > Project Explorer, and then import or export as needed.