MS Access Print Report using VBA

前端 未结 3 729
梦毁少年i
梦毁少年i 2021-01-16 04:00

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 po

3条回答
  •  抹茶落季
    2021-01-16 04:50

    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.

    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.

    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:


    (source: dfenton.com)

    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:

      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.

    For instance, I have this AfterUpdate event behind the Layout option group:

      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.

    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).

    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.

    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.

提交回复
热议问题