I have a very VBA intensive report. When I preview it everything is great but when I print it after previewing things go wacky. I have spent many hours narrowing down the possibilities and I have conclude with a certain level of confidence that it is a bug in MS Access.
我有一份非常详细的报告。当我预览它的时候,一切都很好,但当我在预览后打印出来,就会变得很古怪。我花了很多时间来缩小这些可能性,我以某种程度的信心得出结论,这是MS Access的一个缺陷。
Up to this point my method for printing reports was to open the report using docmd.openreport "report"
. I then use the docmd.printout
command so that I can set the page range, collation etc.
到目前为止,我打印报告的方法是使用docmd打开报告。openreport“报告”。然后使用docmd。打印命令,以便我可以设置页面范围,排序等。
Is there a way to print a report directly and still be able to set options like page rage, collate etc without doing a preview first?
有没有一种方法可以直接打印报表,并且仍然能够在不先预览的情况下设置页面狂怒、collate等选项?
Thanks, Jeff
谢谢,杰夫
3 个解决方案
#1
1
long ago, i had a very difficult case. i had to do some field creations, and moving and formatting and this could only be done one way. i took a bold approach and it turned to be the only way: i opened the report hidden and in design mode, had vba do it's stuff, and when done, the report was changed to normal and visible for display and printing.
很久以前,我遇到了一个非常困难的案子。我必须做一些现场创作,移动和格式化,这只能通过一种方式完成。我采取了一种大胆的方法,它变成了唯一的方法:我打开了隐藏的报告,在设计模式下,让vba做它的事情,当完成时,报告变成了正常的,并且可以看到显示和打印。
#2
3
There is unfortunately no way to do it entirely neatly in code, but it can still be done since the introduction of the WindowMode parameter of the DoCmd.OpenReport method. This makes it possible to open a report in print preview mode and have it be hidden. You can then set properties of the report's Printer object (such as the output printer and orientation), and then use DoCmd.PrintOut to print a page range.
不幸的是,在代码中没有办法完全地做到这一点,但是自从引入DoCmd的WindowMode参数之后,它仍然可以完成。OpenReport方法。这使得可以在打印预览模式下打开报告并将其隐藏起来。然后可以设置报表打印机对象的属性(例如输出打印机和方向),然后使用DoCmd。打印输出以打印一个页面范围。
One thing to note:
有一件事需要注意:
You can't do this in the report's OnOpen event, because changing anything that has an effect on the layout will not give you correct results. For instance, if in the OnOpen event, you changed from Portrait to Landscape orientation, you won't have an accurate count of how many pages there are in the report, because the report hasn't been formated at the time the OnOpen event fires. For everything but pages, though, it's OK.
在报告的OnOpen事件中不能这样做,因为更改对布局有影响的任何东西都不会给您正确的结果。例如,如果在OnOpen事件中,您从竖向更改为横向定位,那么您将无法准确地计算报告中有多少页,因为报告在OnOpen事件触发时尚未形成。不过,除了页面,一切都没问题。
The way I would implement this is with a public function and a dialog form. The function would look something like this:
我的实现方式是使用公共函数和对话框。函数是这样的
Public Function PrintReport(strReport As String) As Boolean
' open report in PREVIEW mode but HIDDEN
DoCmd.OpenReport strReport, acViewPreview, , , acHidden
' open the dialog form to let the user choose printing options
DoCmd.OpenForm "dlgPrinter", , , , , acDialog, strReport
With Forms!dlgPrinter
If .Tag <> "Cancel" Then
Set Reports(strReport).Printer = Application.Printers((!cmbPrinter))
Reports(strReport).Printer.Orientation = !optLayout
Application.Echo False
DoCmd.SelectObject acReport, strReport
DoCmd.PrintOut acPages, !txtPageFrom, !txtPageTo
PrintReport = True
End If
End With
DoCmd.Close acForm, "dlgPrinter"
DoCmd.Close acReport, strReport
Application.Echo True
End Function
The dialog form would look something like this:
对话框形式如下:
alt text http://dfenton.com/DFA/examples/PrinterProperties.png
alt文本http://dfenton.com/DFA/examples/PrinterProperties.png
As you can see above, I open this dialog with an OpenArg parameter, which is the name of the report. In the dialog's OnLoad event, I initialize the controls on the form:
正如您在上面看到的,我使用OpenArg参数打开这个对话框,这是报告的名称。在对话的OnLoad事件中,我初始化窗体上的控件:
Dim varPrinter As Printer
Dim strRowsource As String
Dim strReport As String
If Len(Me.OpenArgs) > 0 Then
strReport = Me.OpenArgs
Me.Tag = strReport
For Each varPrinter In Application.Printers
strRowsource = strRowsource & "; " & varPrinter.DeviceName
Next varPrinter
Me!cmbPrinter.RowSource = Mid(strRowsource, 3)
' first check to see that the report is still open
If (1 = SysCmd(acSysCmdGetObjectState, acReport, strReport)) Then
With Reports(strReport).Printer
Me!cmbPrinter = .DeviceName
Me!optLayout = .Orientation
End With
Me!txtPageTo = Reports(strReport).Pages
End If
End If
I use the form's .Tag property for the report name, and then do everything based on that, including making changes to report properties on the fly, which is possible because the report is open in preview mode, but not visible.
我使用表单的. tag属性作为报表名称,然后根据该属性执行所有操作,包括修改报告属性,这是可能的,因为报告是在预览模式中打开的,但不可见。
For instance, I have this AfterUpdate event behind the Layout option group:
例如,我在Layout选项组后面有这个AfterUpdate事件:
With Reports(Me.Tag)
.Printer.Orientation = Me!optLayout
Me!txtPageTo = .Pages
End With
The reason I change the page range numbers is because changing the orientation will most likely change the number of pages. Unlike in the OnOpen event, changes to a the format properties of a report open invisibly in Print Preview mode happen immediately.
我改变页面范围的原因是因为改变方向很可能会改变页面的数量。与OnOpen事件不同,在打印预览模式中打开的报表格式属性的更改会立即发生。
I use my standard methods for dialog forms, which is to have the Cancel and Continue buttons set the form's .Visible property to False, which allows the calling code to continue. For the Cancel button, I set the form's .Tag property to "Cancel" and check the .Tag property when the code continues in the calling context (see above).
我使用标准的对话框表单方法,即取消和继续按钮设置窗体的。可见属性为False,这允许调用代码继续。对于Cancel按钮,我将表单的. tag属性设置为“Cancel”,并在调用上下文中的代码继续执行时检查. tag属性(见上面)。
So, this isn't as great as it would be to be able to set the page range on the Printer object directly, but it gets the job done.
因此,这并不像能够直接在打印机对象上设置页面范围那样好,但是它可以完成任务。
One thing that would need to be changed in production code is making sure there was an error handler in the PrintReport function so that if something went wrong, Application.Echo can be turned back on (otherwise, the user might be stuck with a blank screen and unable to work). The alternative would be to just let the report appear onscreen when the DoCmd.SelectObject method is invoked. But if I'm hiding the report preview from the user, I would want to go all the way.
在生产代码中需要更改的一件事是确保PrintReport函数中有一个错误处理程序,以便在出错时应用程序。Echo可以重新打开(否则,用户可能会卡在空白屏幕上,无法工作)。另一种选择是让报告在DoCmd时出现在屏幕上。SelectObject方法调用。但是如果我把报告预览从用户那里隐藏起来,我就会想要一直走下去。
For more information on this, you should investigate the .Printer object in the Object Browser (F2 in the VBE), and MS Knowledge Base article 290293 is helpful in explaining the interactions between the Application.Printers collection and Application.Printer object and the ones associated with a particular report. I also found a little tutorial on the Office site that clarified a few things.
有关这方面的更多信息,您应该研究对象浏览器中的. printer对象(VBE中的F2),而MS知识库文章290293有助于解释应用程序之间的交互。打印机收集和应用程序。打印机对象和与特定报告相关的对象。我还在办公室网站上找到了一个小教程,阐明了一些事情。
#3
0
One solution is to set the printer options in the design of the report, save those changes and the print it. The downside is that this will tie the report to a specific printer unless you go into the design and change it.
一种解决方案是在报表的设计中设置打印机选项,保存这些更改并将其打印出来。缺点是,这将把报告绑定到特定的打印机,除非您进入设计并更改它。
DoCmd.OpenReport "ReportName", acViewDesign, Null, Null, acHidden
Dim oRpt As Report
Set oRpt = Reports(0)
oRpt.UseDefaultPrinter = False
oRpt.Printer = Application.Printers("printer name")
With oRpt.Printer
.PaperBin = acPRBNAuto
.PaperSize = acPRPSLetter
.Copies = 1
.PrintQuality = acPRPQMedium
End With
DoCmd.Close acReport, "ReportName", acSaveYes
DoCmd.OpenReport "ReportName", acViewNormal
Set oRpt = Nothing
#1
1
long ago, i had a very difficult case. i had to do some field creations, and moving and formatting and this could only be done one way. i took a bold approach and it turned to be the only way: i opened the report hidden and in design mode, had vba do it's stuff, and when done, the report was changed to normal and visible for display and printing.
很久以前,我遇到了一个非常困难的案子。我必须做一些现场创作,移动和格式化,这只能通过一种方式完成。我采取了一种大胆的方法,它变成了唯一的方法:我打开了隐藏的报告,在设计模式下,让vba做它的事情,当完成时,报告变成了正常的,并且可以看到显示和打印。
#2
3
There is unfortunately no way to do it entirely neatly in code, but it can still be done since the introduction of the WindowMode parameter of the DoCmd.OpenReport method. This makes it possible to open a report in print preview mode and have it be hidden. You can then set properties of the report's Printer object (such as the output printer and orientation), and then use DoCmd.PrintOut to print a page range.
不幸的是,在代码中没有办法完全地做到这一点,但是自从引入DoCmd的WindowMode参数之后,它仍然可以完成。OpenReport方法。这使得可以在打印预览模式下打开报告并将其隐藏起来。然后可以设置报表打印机对象的属性(例如输出打印机和方向),然后使用DoCmd。打印输出以打印一个页面范围。
One thing to note:
有一件事需要注意:
You can't do this in the report's OnOpen event, because changing anything that has an effect on the layout will not give you correct results. For instance, if in the OnOpen event, you changed from Portrait to Landscape orientation, you won't have an accurate count of how many pages there are in the report, because the report hasn't been formated at the time the OnOpen event fires. For everything but pages, though, it's OK.
在报告的OnOpen事件中不能这样做,因为更改对布局有影响的任何东西都不会给您正确的结果。例如,如果在OnOpen事件中,您从竖向更改为横向定位,那么您将无法准确地计算报告中有多少页,因为报告在OnOpen事件触发时尚未形成。不过,除了页面,一切都没问题。
The way I would implement this is with a public function and a dialog form. The function would look something like this:
我的实现方式是使用公共函数和对话框。函数是这样的
Public Function PrintReport(strReport As String) As Boolean
' open report in PREVIEW mode but HIDDEN
DoCmd.OpenReport strReport, acViewPreview, , , acHidden
' open the dialog form to let the user choose printing options
DoCmd.OpenForm "dlgPrinter", , , , , acDialog, strReport
With Forms!dlgPrinter
If .Tag <> "Cancel" Then
Set Reports(strReport).Printer = Application.Printers((!cmbPrinter))
Reports(strReport).Printer.Orientation = !optLayout
Application.Echo False
DoCmd.SelectObject acReport, strReport
DoCmd.PrintOut acPages, !txtPageFrom, !txtPageTo
PrintReport = True
End If
End With
DoCmd.Close acForm, "dlgPrinter"
DoCmd.Close acReport, strReport
Application.Echo True
End Function
The dialog form would look something like this:
对话框形式如下:
alt text http://dfenton.com/DFA/examples/PrinterProperties.png
alt文本http://dfenton.com/DFA/examples/PrinterProperties.png
As you can see above, I open this dialog with an OpenArg parameter, which is the name of the report. In the dialog's OnLoad event, I initialize the controls on the form:
正如您在上面看到的,我使用OpenArg参数打开这个对话框,这是报告的名称。在对话的OnLoad事件中,我初始化窗体上的控件:
Dim varPrinter As Printer
Dim strRowsource As String
Dim strReport As String
If Len(Me.OpenArgs) > 0 Then
strReport = Me.OpenArgs
Me.Tag = strReport
For Each varPrinter In Application.Printers
strRowsource = strRowsource & "; " & varPrinter.DeviceName
Next varPrinter
Me!cmbPrinter.RowSource = Mid(strRowsource, 3)
' first check to see that the report is still open
If (1 = SysCmd(acSysCmdGetObjectState, acReport, strReport)) Then
With Reports(strReport).Printer
Me!cmbPrinter = .DeviceName
Me!optLayout = .Orientation
End With
Me!txtPageTo = Reports(strReport).Pages
End If
End If
I use the form's .Tag property for the report name, and then do everything based on that, including making changes to report properties on the fly, which is possible because the report is open in preview mode, but not visible.
我使用表单的. tag属性作为报表名称,然后根据该属性执行所有操作,包括修改报告属性,这是可能的,因为报告是在预览模式中打开的,但不可见。
For instance, I have this AfterUpdate event behind the Layout option group:
例如,我在Layout选项组后面有这个AfterUpdate事件:
With Reports(Me.Tag)
.Printer.Orientation = Me!optLayout
Me!txtPageTo = .Pages
End With
The reason I change the page range numbers is because changing the orientation will most likely change the number of pages. Unlike in the OnOpen event, changes to a the format properties of a report open invisibly in Print Preview mode happen immediately.
我改变页面范围的原因是因为改变方向很可能会改变页面的数量。与OnOpen事件不同,在打印预览模式中打开的报表格式属性的更改会立即发生。
I use my standard methods for dialog forms, which is to have the Cancel and Continue buttons set the form's .Visible property to False, which allows the calling code to continue. For the Cancel button, I set the form's .Tag property to "Cancel" and check the .Tag property when the code continues in the calling context (see above).
我使用标准的对话框表单方法,即取消和继续按钮设置窗体的。可见属性为False,这允许调用代码继续。对于Cancel按钮,我将表单的. tag属性设置为“Cancel”,并在调用上下文中的代码继续执行时检查. tag属性(见上面)。
So, this isn't as great as it would be to be able to set the page range on the Printer object directly, but it gets the job done.
因此,这并不像能够直接在打印机对象上设置页面范围那样好,但是它可以完成任务。
One thing that would need to be changed in production code is making sure there was an error handler in the PrintReport function so that if something went wrong, Application.Echo can be turned back on (otherwise, the user might be stuck with a blank screen and unable to work). The alternative would be to just let the report appear onscreen when the DoCmd.SelectObject method is invoked. But if I'm hiding the report preview from the user, I would want to go all the way.
在生产代码中需要更改的一件事是确保PrintReport函数中有一个错误处理程序,以便在出错时应用程序。Echo可以重新打开(否则,用户可能会卡在空白屏幕上,无法工作)。另一种选择是让报告在DoCmd时出现在屏幕上。SelectObject方法调用。但是如果我把报告预览从用户那里隐藏起来,我就会想要一直走下去。
For more information on this, you should investigate the .Printer object in the Object Browser (F2 in the VBE), and MS Knowledge Base article 290293 is helpful in explaining the interactions between the Application.Printers collection and Application.Printer object and the ones associated with a particular report. I also found a little tutorial on the Office site that clarified a few things.
有关这方面的更多信息,您应该研究对象浏览器中的. printer对象(VBE中的F2),而MS知识库文章290293有助于解释应用程序之间的交互。打印机收集和应用程序。打印机对象和与特定报告相关的对象。我还在办公室网站上找到了一个小教程,阐明了一些事情。
#3
0
One solution is to set the printer options in the design of the report, save those changes and the print it. The downside is that this will tie the report to a specific printer unless you go into the design and change it.
一种解决方案是在报表的设计中设置打印机选项,保存这些更改并将其打印出来。缺点是,这将把报告绑定到特定的打印机,除非您进入设计并更改它。
DoCmd.OpenReport "ReportName", acViewDesign, Null, Null, acHidden
Dim oRpt As Report
Set oRpt = Reports(0)
oRpt.UseDefaultPrinter = False
oRpt.Printer = Application.Printers("printer name")
With oRpt.Printer
.PaperBin = acPRBNAuto
.PaperSize = acPRPSLetter
.Copies = 1
.PrintQuality = acPRPQMedium
End With
DoCmd.Close acReport, "ReportName", acSaveYes
DoCmd.OpenReport "ReportName", acViewNormal
Set oRpt = Nothing