Search code examples
vbams-wordmailmerge

How can get a list of included records in a Word Mail Merge Document?


I have a Mail Merge Document which is controlled via VBA. After the user selects the records he wants to print, I want these to get a print date set in the database. For that I need a list of the records included in the mail merge.

I tried using the .Included property, setting ThisDocument.MailMerge.DataSource.ActiveRecord = wdFirstRecord (I later removed the wdFirstRecord in favour of 1 since it was giving me trouble) and then checking for ThisDocument.MailMerge.DataSource.Included to be true but got a 5852 runtime-error "Object not available". EDIT: I used the following code to iterate over the records. When I remove either of the commented .Included statements, I get said error. (The execution does not feel as slow as yesterday anymore, though it is not especially fast.)

Function outputRecords(Optional limitRecords = -1)
    With ThisDocument.MailMerge.DataSource
    Dim str
        For currentFieldNameIndex = 1 To .FieldNames.Count
            str = str & .FieldNames(currentFieldNameIndex) & vbTab
        Next
        Debug.Print str

        For currentRecordIndex = 1 To .RecordCount
            If currentRecordIndex <= limitRecords Or limitRecords < 0 Then
                .ActiveRecord = currentRecordIndex
                str = ""
                For currentDataFieldIndex = 1 To .DataFields.Count
                    str = str & .DataFields(currentDataFieldIndex) & vbTab
                Next
                'Debug.Print str
            End If
            '.Included = True
            'Debug.Print .Included
        Next

    End With
End Function

Is there a solution to know which records are selected by the user?

About my document: After some general computation, the data source is linked to the document using

Dim sql As String
sql = "SELECT * FROM `Sheet0$` "
sql = sql & "WHERE ((`" & photoPathHeader & "` > '') AND (`" & photoLastEditHeader & "` >= #" & Format(printFromDate, "yyyy-mm-dd") & "#)) "
sql = sql & "ORDER BY `klasse#name` ASC"
ThisDocument.MailMerge.OpenDataSource _
    name:=ThisDocument.Path & "\" & ThisDocument.Variables("masterDataFileName"), _
    SQLStatement1:=sql, _
    ReadOnly:=True, LinkToSource:=True

a dialog where the user can select individual records for printing is shown. I used this code for that:

Application.Dialogs(wdDialogMailMergeRecipients).Display

And finally, the Mail Merge is executed using

ThisDocument.MailMerge.Execute

Many thanks in advance!


Solution

  • Thanks to @yokki's answer, I was able to combine that with what I know and ended up with the following

    Sub listIncluded()
        Dim LastRecord As Long
        With ActiveDocument.MailMerge.DataSource
    
        ' Storing the index of the last record for later use
        .ActiveRecord = wdLastRecord
        LastRecord = .ActiveRecord
    
        .ActiveRecord = wdFirstRecord
            Do
                Debug.Print .ActiveRecord
                DoEvents
                If .ActiveRecord = LastRecord Then Exit Do
                .ActiveRecord = wdNextRecord
            Loop Until .ActiveRecord > LastRecord ' Note this will never be satisfied and the loop will always be exited via the Exit Do statement
        End With
    End Sub
    

    I find the WdMailMergeActiveRecord enumeration a bit confusing but this works.

    The original answer threw a 5853 Invalid parameter error on the wdNextRecord if the last included record had already been reached and was not equal to the last record overall (i.e. the last record was not included). I circumvented this issue by storing the index of the last record into a local variable and using that to check if the last included record was reached.

    Thank you very much to everyone who helped!

    As briefly pointed out in a comment, I already have a list of all the data in the data source as an array since I also use it in other parts of the script. I used the following statement for that

    numRows = wks.Range("A1").CurrentRegion.Rows.Count
    masterData = wks.Range("A1").CurrentRegion.Offset(RowOffset:=1).Resize(RowSize:=numRows).Value
    

    wks refers to an Excel worksheet object.