MS Access Print Report using VBA - ms-access

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.
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.
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?
Thanks,
Jeff

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.

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.

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

Related

How to close a form onclick in Access

In Access 2007, I have a button that opens up a form in dialog mode (modal and popup set to true). I'm trying to save the data from that form and then close that form like so:
Private Sub Close_Click()
DoCmd.Close acForm, Me.Form, acSaveYes
End Sub
The purpose is to add a new record to a table. It accomplishes this, new data is added to the database based on the inputs. However, the form still stay visible. I have also tried this not using dialog mode, and the result has been the same.
You need to pass DoCmd.Close the form name, not the actual form object.
Private Sub Close_Click()
DoCmd.Close acForm, Me.Name, acSaveYes
End Sub
Also, while you're making changes, add Option Explicit add the top of your module. This would've made it way easier to spot the bug. Read here why having Option Explicit on is a good idea.

ACCESS 2010 VBA web browser control source setfocus

I've been searching for an answer to this for several weeks without a solution.
The goal: I'm working on a script to scan a PDF barcode into a textbox. The PDF is displayed in a web browser control. After scanning the barcode, the PDF page advances and I would like to advance the focus to the next textbox to scan the barcode on the new page.
The problem: When I change the control source for the web browser (which occurs in the change event for the textbox), the focus is immediately set to another control on the userform (a combobox with tab index 1). How do I bring the focus back to the textbox?
It sounds simple but I've tried so many things. Setting the web browser control source seems to refresh the form but it doesn't call a form load event or anything like that. The closest I've come was using the enter event for the combobox that gets focus, which only works when stepping through the code.
Private Sub txtQR1_Change() 'advance focus and PDF page after scan
If isClean = False Then
If Len(Me.txtQR1.Text) = 16 Then
Me.txtQR1.Text = Clean(Me.txtQR1.Text) 'remove special characters & carriage returns
txtQR_Focus = True
txtQR_Identifier = 1
If intBarcodePages > txtQR_Identifier Then
Me.wbVendor.ControlSource = "=(" & newFilePath & "#page=2" & """" & ")"
End If
End If
End If
End Sub
EDIT 5-5-15: This is essentially the same issue I'm having. http://www.experts-exchange.com/Database/MS_Access/Q_28164666.html
I was finally able to resolve this issue using a simple "DoEvents" in the Enter event for the combobox that was stealing the focus. If anyone can explain why this works, please do so.
Thanks to those who have given this any thought! :)
EDIT (Apr 27, 2015):
This solution only seems to work for my pc. I've tested on several other machines now and the focus is going back to the web browser control. Again, the focus remains in the textbox when debugging but otherwise does not. Sometimes the cursor even APPEARS to be in the textbox(the cursor is there but not blinking) although the web browser actually has the focus.
EDIT (May 6, 2015): The current working solution is through the Form Timer event and an API that brings the focus from the web browser control back to the Access application.
Public Declare Function SetWinFocus Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long
Public Sub Form_Timer()
With Forms!frmMain
SetWinFocus Application.hWndAccessApp
.TimerInterval = 0
.SetFocus
Select Case txtQR_Identifier
Case 0
.txtQR1Blank.SetFocus
etc...
End Select
End With
End Sub
Then calling the timer event just after the web browser control source is changed.
Me.wbVendor.ControlSource = "=(" & newFilePath & "#page=" & intBlankPage2 & """" & ")"
Me.TimerInterval = 500
I hope this helps anyone else having problems with the web browser focus issue.
My solution was different. On the Browser object, set the on focus event to switch the focus back to the last control with focus. The code is pretty short:
Private Sub Browser_GotFocus()
On Error Resume Next
Screen.PreviousControl.SetFocus
End Sub
The browser still works normally with the mouse.

ms access display inputs of textbox1 in textbox2 in realtime

i'm working on some VBA code for msaccess, basically i want to display the data i typed on textbox1 to textbox2. i have tried all the events available and it ain't work.
i have tried this
Private Sub input1_AfterUpdate()
output1.Value = input1.Value
End Sub
on key down and after update. it works on after update but i need to refresh the page before the inputs are loaded to textbox2.
what i need is realtime update, everytime i typed something on textbox1, it will display on textbox2, including the previous letters i typed.
thanks a lot guys, badly need a solution for this :(
Use Change event and Text property:
Private Sub textbox1_Change()
textbox2.Value = textbox1.Text
End Sub
We take .Text property of source textbox, because .Value is changed after the end of edition.

Openning a new form from a tab

I'm creating forms with VBA/Access to access my database.
In a form, I have a *lst_sinistres* listbox that displays the results of my SQL query and when I doubleclick on one of the results it opens me another form with thanks to this code
Private Sub lst_sinistres_DblClick(Cancel As Integer)
DoCmd.OpenForm "F_SINISTRE_MRH", acNormal, , , , , Me.lst_sinistres.Value
End Sub
I wanted to change my form, and add tabs to make it more ergonomic. So I placed my *lst_sinistres* listbox inside a tab.
The problem is that when I doubleclick on one of the results in this listbox (now placed in the tab), the form *F_SINISTRE_MRH* does not open.
Does someone have an idea of ​​where the problem might come?
Thank you
A quirk of VBA control events is that event code can become detached from the control object. Things that cause this tend to be re-naming controls and copy/pasting similar code between controls. To move your listbox onto a tab control you needed to cut and paste it temporarily. That broke the link between the written code and the object name. When the code and object are properly linked, [Event Procedure] shows up in the property sheet (as suggested by #4dmonster).
If you are in the VBA editor, choosing Debug->Compile will search through all the code and re-link event code with like-named controls. This step is worth a try before re-writing because you may end up with orphan blocks of
Private Sub OldControlName_DblClick(Cancel As Integer)
MsgBox "Why don't I work anymore?"
End Sub
that are treated as Form-level subroutines that just happen to never be called.
pT

Event not Firing in MS Access VBA

I have a form in MS Access which has an image. The image has an Click event which opens a modal form. The modal form has an OK and Cancel button. When you click the OK button, an event is supposed to fire which tells the main form which button was clicked. (This is to simulate the DialogResult functionality in C#). However, the code in the event handler never runs.
The modal form has the following in the general declarations:
Public Event OnDialogBoxClose(NewRecordID As Long, DialogResult As DialogResults)
and the following code where the OK button is clicked:
RaiseEvent OnDialogBoxClose(NewHardwareBaseItemID, dlgresBtnOKClicked)
The main form has the following in the general declarations:
Dim WithEvents RespondQuickAddClose As Form_qckfrmHardwareBaseItemCreate
and the following event handler:
Private Sub RespondQuickAddClose_OnDialogBoxClose(NewRecordID As Long, DialogResult As DialogResults)
MsgBox "Responding to closing of the dialog box" 'Never happens
Me.Requery
End Sub
Can someone explain why the event handler is never called?
Thanks!
Background:
The purpose of all this is to allow a modal dialog box to add an entry, then return the ID of the entry back to the main form to set the value of controls. For instance, imagine you are filling out an insurance form, and you need to select a brand of car this is not there. You click on an icon which pops up with the modal dialog box to allow you to add the car brand. Then when you click OK, it takes you back to the insurance form and selects the brand of car you just created.
This follows an example I found here:
http://database.itags.org/ms-access-database/80292/
You're making your life way too complicated by applying concepts from a different development environment to Access VBA. While VBA does support WithEvents/RaiseEvent, there's no reason to get that complicated here.
The usual way to work with dialogs in Access is that instead of closing them, you hide them. This allows the code after the form was open to run while leaving the values in the form available for use in that code.
Sample code in the OnOpen event of a report that opens a form for collecting values to filter the report:
Private Sub Report_Open(Cancel As Integer)
DoCmd.OpenForm "dlgDateRange", , , , , acDialog, "ThisYear"
If IsLoaded("dlgDateRange") Then
With Forms!dlgDateRange
If .Tag = "Cancel" Then
Cancel = True
Else
Me.Filter = "[InvoiceDate] Between #" & !txtStart & "# AND #" & !txtEnd & "#"
Me.FilterOn = True
Me!lblDateRange.Caption = StrConv(Trim(("from " + varZLStoNull(Format(!txtStart, "mm/dd/yyyy"))) _
& (" to " + varZLStoNull(Format(!txtEnd, "mm/dd/yyyy")))), vbProperCase)
End If
End With
DoCmd.Close acForm, "dlgDateRange"
End If
End Sub
The dialog form has two command buttons, CONTINUE>> and CANCEL. The CANCEL button sets the form's tag to "Cancel" and sets the form's .Visible property to False. The CONTINUE>> button does nothing but set the form's .Visible property to False. Clicking either of those buttons allows the code to continue on the line after the form is open with the acDialog switch.
My philosophy is to make the dialogs as stupid as possible. The calling code has to know what it's looking for in the forms (i.e., you need to know the names of the controls you're reading data out of), but that could be gotten around by adding customer properties to the form. But then you have to know the property names, so you've just moved the ball.
I have also implemented this kind of thing by wrapping the dailog form in a class module, and then the calling context simply initializes an instance of the class and then pulls the values out of it at the appropriate time. But that's actually more complicated that the approach above.
well I don't agree to
"While VBA does support WithEvents/RaiseEvent, there's no reason to
get that complicated here."
I have worked on various VB6 and VBA project. Recently I coded VBA in excel where I raised an event from winform. Few things to be considered when doing so.
If you are calling non-modal winform in VBA with
withevents/raiseevent. It should work normally as expected. No major
workaround is needed
If you are calling modal winform in VBA. Withevents/raiseevents may
not function as per requirement. A quick workaround is to transfer data using public variables declared in the module file.
You will need to use the workaround and I believe it will work absolutely fine.