如何获取 Word 邮件合并文档中包含的记录列表?
How can get a list of included records in a Word Mail Merge Document?
我有一个通过 VBA 控制的邮件合并文档。在用户 selects 他想要打印的记录之后,我希望这些记录在数据库中设置打印日期。为此,我需要邮件合并中包含的记录列表。
我尝试使用 .Included property,设置 ThisDocument.MailMerge.DataSource.ActiveRecord = wdFirstRecord
(我后来删除了 wdFirstRecord
以支持 1
,因为它给我带来了麻烦)
然后检查 ThisDocument.MailMerge.DataSource.Included
是否为真,但出现 5852 运行时错误 "Object not available"。 编辑: 我使用以下代码遍历记录。当我删除任何一个评论的 .Included 语句时,我得到了错误。 (执行感觉不像昨天那么慢了,虽然不是特别快。)
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
是否有解决方案可以知道用户 select编辑了哪些记录?
关于我的文档:经过一些一般计算,数据源链接到文档使用
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
显示一个对话框,用户可以在其中 select 打印单个记录。我为此使用了这段代码:
Application.Dialogs(wdDialogMailMergeRecipients).Display
最后,使用
执行邮件合并
ThisDocument.MailMerge.Execute
非常感谢!
此答案基于目前的评论。
- 我不明白 .Included here 的问题,除非 ThisDocument 是错误的文档。但是,由于您的循环代码在访问 ThisDocument.MailMerge.DataSource 的其他成员时似乎没有抛出错误,因此问题不明显。但是,我认为您不需要使用 .Included.
- 当我尝试访问任何 .Datasource.DataFields 项目时,我立即发现速度变慢了。我不知道为什么会这样。每次访问都有延迟,而不仅仅是每条记录的第一次访问。我的一个想法是,一旦您更改活动记录,Word 就会刷新主文档中合并字段的值,这可能会导致问题,特别是如果 Word 出于某种原因需要访问打印机驱动程序时。但我没有尝试(例如将视图更改为草稿)改变这一点。
你真的不需要 .Included 因为你可以像这样迭代记录:
With ActiveDocument.MailMerge.DataSource
previousRecord = 0
.ActiveRecord = wdFirstRecord
While .ActiveRecord <> previousRecord
previousRecord = .ActiveRecord
Debug.Print .ActiveRecord ' (just lists the record number)
previousRecord = .ActiveRecord
.ActiveRecord = wdNextRecord
DoEvents ' advisable if you need to stop the code
Wend
如果您需要迭代所有记录,那么您需要改用wdFirstDataSourceRecord和wdNextDataSourceRecord。
我能够加快访问数据的唯一方法是修改文档以包含我想列出的所有字段,然后从文档而不是从 .DataSource.Datafields。我个人认为修改 Mail Merge 主文档并不理想——特别是,有些东西可能会阻止您在开头插入 material,就像我所做的那样。但是我的测试代码(需要更多错误捕获等)是这样的:
Sub checkincluded()
Dim b As Word.Bookmark
Dim bVMMFC As Boolean
Dim bSFC As Boolean
Dim i As Integer
Dim previousRecord As Long
Dim p As Word.Paragraph
Dim r As Word.Range
With ActiveWindow.View
bSFC = .ShowFieldCodes
.ShowFieldCodes = False
End With
With ActiveDocument.MailMerge
bVMMFC = .ViewMailMergeFieldCodes
.ViewMailMergeFieldCodes = False
With .DataSource
Set r = ActiveDocument.Range(0, 0)
Set p = ActiveDocument.Range(0, 0).Paragraphs.Add
For i = 1 To .FieldNames.Count
If i > 1 Then
r.Text = vbTab
r.Start = p.Range.End - 1
r.End = p.Range.End - 1
End If
r.Fields.Add r, WdFieldType.wdFieldMergeField, .FieldNames(i), False
r.Start = p.Range.End - 1
r.End = p.Range.End - 1
Next
r.Start = 0
r.End = p.Range.End - 1
Set b = r.Bookmarks.Add("recorddata")
Set r = Nothing
previousRecord = 0
.ActiveRecord = wdFirstRecord
While .ActiveRecord <> previousRecord
previousRecord = .ActiveRecord
Debug.Print .ActiveRecord, b.Range.Text
previousRecord = .ActiveRecord
.ActiveRecord = wdNextRecord
DoEvents
Wend
b.Delete
Set b = Nothing
p.Range.Text = ""
Set p = Nothing
End With
.ViewMailMergeFieldCodes = bVMMFC
End With
ActiveWindow.View.ShowFieldCodes = bSFC
End Sub
最后,可能值得指出的是,还有其他方法可以排除记录,例如使用 NEXT、NEXTIF、SKIP 和 SKIPIF 字段。但这也许是另一个问题。
感谢@yokki 的回答,我能够将其与我所知道的结合起来并得到以下结果
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
我发现 WdMailMergeActiveRecord enumeration 有点令人困惑,但它有效。
如果最后一个包含的记录已经到达并且不等于最后一个记录整体(即不包括最后一个记录),则原始答案在 wdNextRecord
上抛出 5853 Invalid parameter
错误.我通过将最后一条记录的索引存储到局部变量中并使用它来检查是否到达了最后一条包含的记录来规避了这个问题。
非常感谢大家的帮助!
正如评论中简要指出的那样,我已经列出了所有
数据源中的数据作为数组,因为我也在其他部分使用它
的脚本。我为此使用了以下语句
numRows = wks.Range("A1").CurrentRegion.Rows.Count
masterData = wks.Range("A1").CurrentRegion.Offset(RowOffset:=1).Resize(RowSize:=numRows).Value
wks
引用了一个 Excel 工作表对象。
我有一个通过 VBA 控制的邮件合并文档。在用户 selects 他想要打印的记录之后,我希望这些记录在数据库中设置打印日期。为此,我需要邮件合并中包含的记录列表。
我尝试使用 .Included property,设置 ThisDocument.MailMerge.DataSource.ActiveRecord = wdFirstRecord
(我后来删除了 wdFirstRecord
以支持 1
,因为它给我带来了麻烦)
然后检查 ThisDocument.MailMerge.DataSource.Included
是否为真,但出现 5852 运行时错误 "Object not available"。 编辑: 我使用以下代码遍历记录。当我删除任何一个评论的 .Included 语句时,我得到了错误。 (执行感觉不像昨天那么慢了,虽然不是特别快。)
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
是否有解决方案可以知道用户 select编辑了哪些记录?
关于我的文档:经过一些一般计算,数据源链接到文档使用
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
显示一个对话框,用户可以在其中 select 打印单个记录。我为此使用了这段代码:
Application.Dialogs(wdDialogMailMergeRecipients).Display
最后,使用
执行邮件合并ThisDocument.MailMerge.Execute
非常感谢!
此答案基于目前的评论。
- 我不明白 .Included here 的问题,除非 ThisDocument 是错误的文档。但是,由于您的循环代码在访问 ThisDocument.MailMerge.DataSource 的其他成员时似乎没有抛出错误,因此问题不明显。但是,我认为您不需要使用 .Included.
- 当我尝试访问任何 .Datasource.DataFields 项目时,我立即发现速度变慢了。我不知道为什么会这样。每次访问都有延迟,而不仅仅是每条记录的第一次访问。我的一个想法是,一旦您更改活动记录,Word 就会刷新主文档中合并字段的值,这可能会导致问题,特别是如果 Word 出于某种原因需要访问打印机驱动程序时。但我没有尝试(例如将视图更改为草稿)改变这一点。
你真的不需要 .Included 因为你可以像这样迭代记录:
With ActiveDocument.MailMerge.DataSource previousRecord = 0 .ActiveRecord = wdFirstRecord While .ActiveRecord <> previousRecord previousRecord = .ActiveRecord Debug.Print .ActiveRecord ' (just lists the record number) previousRecord = .ActiveRecord .ActiveRecord = wdNextRecord DoEvents ' advisable if you need to stop the code Wend
如果您需要迭代所有记录,那么您需要改用wdFirstDataSourceRecord和wdNextDataSourceRecord。
我能够加快访问数据的唯一方法是修改文档以包含我想列出的所有字段,然后从文档而不是从 .DataSource.Datafields。我个人认为修改 Mail Merge 主文档并不理想——特别是,有些东西可能会阻止您在开头插入 material,就像我所做的那样。但是我的测试代码(需要更多错误捕获等)是这样的:
Sub checkincluded() Dim b As Word.Bookmark Dim bVMMFC As Boolean Dim bSFC As Boolean Dim i As Integer Dim previousRecord As Long Dim p As Word.Paragraph Dim r As Word.Range With ActiveWindow.View bSFC = .ShowFieldCodes .ShowFieldCodes = False End With With ActiveDocument.MailMerge bVMMFC = .ViewMailMergeFieldCodes .ViewMailMergeFieldCodes = False With .DataSource Set r = ActiveDocument.Range(0, 0) Set p = ActiveDocument.Range(0, 0).Paragraphs.Add For i = 1 To .FieldNames.Count If i > 1 Then r.Text = vbTab r.Start = p.Range.End - 1 r.End = p.Range.End - 1 End If r.Fields.Add r, WdFieldType.wdFieldMergeField, .FieldNames(i), False r.Start = p.Range.End - 1 r.End = p.Range.End - 1 Next r.Start = 0 r.End = p.Range.End - 1 Set b = r.Bookmarks.Add("recorddata") Set r = Nothing previousRecord = 0 .ActiveRecord = wdFirstRecord While .ActiveRecord <> previousRecord previousRecord = .ActiveRecord Debug.Print .ActiveRecord, b.Range.Text previousRecord = .ActiveRecord .ActiveRecord = wdNextRecord DoEvents Wend b.Delete Set b = Nothing p.Range.Text = "" Set p = Nothing End With .ViewMailMergeFieldCodes = bVMMFC End With ActiveWindow.View.ShowFieldCodes = bSFC End Sub
最后,可能值得指出的是,还有其他方法可以排除记录,例如使用 NEXT、NEXTIF、SKIP 和 SKIPIF 字段。但这也许是另一个问题。
感谢@yokki 的回答,我能够将其与我所知道的结合起来并得到以下结果
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
我发现 WdMailMergeActiveRecord enumeration 有点令人困惑,但它有效。
如果最后一个包含的记录已经到达并且不等于最后一个记录整体(即不包括最后一个记录),则原始答案在 wdNextRecord
上抛出 5853 Invalid parameter
错误.我通过将最后一条记录的索引存储到局部变量中并使用它来检查是否到达了最后一条包含的记录来规避了这个问题。
非常感谢大家的帮助!
正如评论中简要指出的那样,我已经列出了所有 数据源中的数据作为数组,因为我也在其他部分使用它 的脚本。我为此使用了以下语句
numRows = wks.Range("A1").CurrentRegion.Rows.Count
masterData = wks.Range("A1").CurrentRegion.Offset(RowOffset:=1).Resize(RowSize:=numRows).Value
wks
引用了一个 Excel 工作表对象。