I am using David-W-Fenton's answer to this question to try to allow users to print a report when they click on a control, but I am getting the following error message:
runtime error 2448: you cannot assign a value to this object.
The line of code triggering the error is:
Me.txtPageTo.Value = numPages
Here is the full code of the OnLoad event handler for the form:
Private Sub Form_Load()
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
Dim numPages As String
numPages = Reports(strReport).Pages
Debug.Print "numPages = " & numPages
TypeName(Me.txtPageTo) 'added this line as a test after
Me.txtPageTo.Value = numPages
End If
End If
End Sub
numPages prints out to equal 0 just before the error is thrown, even though the report should have at least one page. Also, the error does not get thrown when the form is already open. The error only gets thrown when the form has to be opened. (Probably because the offending code is in the onload event.)
When I added TypeName(Me.txtPageTo) , it triggered runtime error 2424: The expression you entered has a field, control, or property name that mydatabasename cant find.
I think the problem is that I need to set the control source for txtPageFrom and txtPageTo. But how do I do that? This form will only be loaded by a vba method that is trying to print a report, so the values for txtPageFrom and txtPageTo will come from the calling method, not from some underlying data table. (there is no underlying data table for this dialog form.)
cmbPrinter and optLayout seem to be populating correctly using the code above.
Can anyone show me how to get past the error messages so that the form loads properly?
CORRECT, COMPLETE ANSWER:
The answer to this problem was to greatly simplify the code. The code in the link at the top of this posting is WAY TOO COMPLEX, and tries to reinvent things that are already done well by the tools built into Access and VBA. I was able to print reports without any of the complicated solutions by simply using the following code in the on-click event of a control on a form associated with the report:
Private Sub txtPrintReport_Click()
On Error GoTo Error_Handler
Dim commNum As Long
commNum = Me.CommunicationNumber
Dim strReport As String
Dim strVarName As String
strReport = "rptCommunicationFormForPrinting"
strVarName = "CommunicationNumber"
DoCmd.OpenReport strReport, acViewPreview, , strVarName & " = " & commNum, acWindowNormal, acHidden
DoCmd.RunCommand acCmdPrint
DoCmd.Close acReport, strReport
Exit_Point:
Exit Sub
Error_Handler: 'this handles the case where user clicks cancel button
If Err.Number <> 2501 Then
MsgBox Err.Description, _
vbExclamation, "Error " & Err.Number
End If
DoCmd.Close acReport, strReport
End Sub
That is all the code that was required. Much less than the link at the start of this question. And also much less than the answer below. I am marking John Bingham's answer as the accepted answer because he clearly worked a lot on this, and I am very grateful for that. But I ended up solving the problem with A LOT LESS CODE. My solution does not require the custom dialog form because it uses the Windows print dialog form. As such, the on load event code in my posting (taken from the link above), is not necessary.
The code posted cannot work, because when a form is opened as a dialog:
DoCmd.OpenForm "dlgPrinter", , , , , acDialog, strReport
no other logic after this line is executed until the form has been closed, at which point control returns to the next line after this one, and logic continues on - except of course you can no longer reference anything in that form, because it is now closed.
Ok, now there is a question, where is the button the user clicks to print a report?
What I'm thinking is to split the logic in PrintReport into two methods, one that launches it, and tells the form to configure itself (doing the stuff suggested in the comment), but the rest of PrintReport then needs to happen after the user clicks OK (or Cancel);
So if I assume that you've got a form which can launch one or more reports, and the button is on this form, what I would suggest is this:
In the click event for that button - no changes to what you've got.
In that buttons form, add this:
Public Sub DialogAccept()
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 Sub
Change PrintReport to:
Public Function PrintReport(strReport As String, strVarName As String, numVal As Long) As Boolean
' open report in PREVIEW mode but HIDDEN
DoCmd.OpenReport strReport, acViewPreview, , strVarName & " = " & numVal, acHidden
'DoCmd.OpenReport strReport, acViewPreview, , , acHidden
' open the dialog form to let the user choose printing options
DoCmd.OpenForm "dlgPrinter", , , , , , strReport
Forms!dlgPrinter.Configure
End Function
In the OK/Cancel button click events on dlgPrinter put (after existing code, but removing any instances of "Docmd.close"):
Forms!Calling_Form_Name.DialogAccept
This then calls that method, to do the stuff that is meant to happen after the user says "I'm done with the dialog".
Finally add the configure method to dlgPrinter:
Public Sub Configure()
With Reports(Me.Tag).Printer
Me!cmbPrinter = .DeviceName
Me!optLayout = .Orientation
End With
Dim numPages As String
numPages = Reports(Me.Tag).Pages
Debug.Print "numPages = " & numPages
TypeName(Me.txtPageTo) 'added this line as a test after
Me.txtPageTo.Value = numPages
End Sub
And remove this code section from Form_Load.
Hoep this helps.
Lastly, if the form on which the button launching dlgPrinter can vary, change this line:
DoCmd.OpenForm "dlgPrinter", , , , , acDialog, strReport
to:
DoCmd.OpenForm "dlgPrinter", , , , , acDialog, strReport & ";" & me.Name
In form_load of dlgPrinter, break up me.Openargs using left() & mid() with instr(), and save me.Name into the tag of something on the form (which you're not already using the tag of).
In the OK/Cancel button click events, change the code above to:
Forms(Object.Tag).DialogAccept
Related
In my MS-Access Database I Got a LoginForm, which checks If a User has Access to a specific form or Not.
My main, called "NavigationsFormular" has 4 Tabs
A normal User can access the 1st tab called "Bautagesbericht".
A ControlingUser can access the 2nd, 3d, 4th tab, but not the 1st!
The Login Form is a pop-up at start, I dont want to change it to a tab too,that would be ugly and not optimal.
After the Login succeeded, it loads up the Navigationform.
For the normal user its no problem, but for the controlling part it always says " No access " because it tried to open a tab he's not permitted to.
Thats the Code btw:
If Globals.UserAccess(Me.Name) = False Then
MsgBox " No access!"
DoCmd.Close acForm, Me.Name
End If
Now my idea was, that the Login Form open the form, which the user got permissions to by following code:
If Globals.UserAccess("frm_Räumstellenerfassung") = False Then
DoCmd.BrowseTo ObjectType:=acBrowseToForm, _
ObjectName:="frm_Taetigkeitseingabe_Büro", _
PathToSubformControl:="Navigationsformular.NavigationsUnterformular>frm_Taetigkeitseingabe_Büro.NavigationsUnterformular"
DoCmd.Close acForm, Me.Name
Else
DoCmd.OpenForm "Navigationsformular"
DoCmd.Close acForm, Me.Name
End If
End Sub
The Problem is that the Path is not right..
Hauptformular1 / MainFormular1 = "NavigationsFormular"
Unterformular1 / SubForm1 = "NavigationsUnterformular" (in German)
Formular1 = "frm_Taetigkeitseingabe_Büro"
Tried all combinations nothing helped.
BTW: A Button in my Naviagtion form with this Code is working. Apparently not when trying from an other form.
DoCmd.BrowseTo acBrowseToForm, "frm_Taetigkeitseingabe_Büro","Navigationsformular.NavigationsUnterformular"
ok I followed #June7 tipps. thanks for that. Here my solution:
Edited my form "frm_Räumstellenerfassung" ( Bautagesbericht) to
Private Sub Form_Open(Cancel As Integer)
DoCmd.GoToRecord , , acNewRec
If Globals.UserAccess("frm_Räumstellenerfassung") = False Then
DoCmd.BrowseTo acBrowseToForm, "frm_Taetigkeitseingabe_Büro", "Navigationsformular.NavigationsUnterformular"
End If
End Sub
So msg " No Access " is not popping up anymore and if the User has no rights to see this form, its browsing to the 2nd tab(Tätigkeitserfassung) without seeing any details of the 1st form.
My Login button just navigate to the navigationform
DoCmd.OpenForm "Navigationsformular"
DoCmd.Close acForm, Me.Name
I have Combobox that lets users either edit the related values for this field
- cboBEA using a button. I decided to use a NotInList routine whenever they want to ADD a new value. The edit part works without a hitch, but the NotInList part needs to accept a value after providing related fields in a popup form called frmBEA_JDIR
At first the requery of cboBEA wasn't working, so I did a more deliberate resetting of the rowsource by first setting it to "" then the actual SQL of the rowsource.
Here is the code in the "Save" button of the popup form frmBEA_JDR
Private Sub cmdSave_Click()
Select Case Me.OpenArgs
Case "Edit"
DoCmd.Save
DoCmd.Close acForm, "frmBEA_JDIR"
With Form_sfm_AddSPDistro
.cboBEA.Requery
.cboBESA.Requery
.cmbPROGRAM.RowSource = ""
.cmbPROGRAM.RowSource = "SELECT * FROM qLU_BEA_JDIR;"
.cmbPROGRAM = .cboBEA
End With
Case "AddNew"
Dim strSQL As String
strSQL = "SELECT LU_BEA_JDIR.ID, LU_BEA_JDIR.BEA, LU_BEA_JDIR.BESA, LU_BEA_JDIR.ORGANIZATION " _
& "FROM LU_BEA_JDIR;"
With Form_sfm_AddSPDistro
'cboBEA.Requery doesn't work, so...
.cboBEA.RowSource = ""
.cboBEA.RowSource = strSQL
.cboBESA.Requery
.cboBEA.Value = Me.txtBEA
.cmbPROGRAM.RowSource = ""
.cmbPROGRAM.RowSource = "SELECT * FROM qLU_BEA_JDIR;"
.cmbPROGRAM = .cboBEA
End With
DoCmd.Close acForm, "frmBEA_JDIR"
End Select
End Sub
Here is the NotInList event of the calling form:
Private Sub cboBEA_NotInList(NewData As String, Response As Integer)
Dim MsgBoxAnswer As Variant
Response = acDataErrContinue
Me!cboBEA.Undo 'Used this to prevent the requery error caused by frmBEA_JDIR
MsgBoxAnswer = MsgBox(NewData & " is not in the list. Do you want to add it?", vbQuestion + vbYesNo, "Add " & NewData & "?")
If MsgBoxAnswer = vbNo Then
Me.cboBEA = Null
DoCmd.GoToControl "cboBEA"
Else
DoCmd.OpenForm "frmBEA_JDIR", acNormal, , , acFormAdd, , "AddNew"
Form_frmBEA_JDIR.txtBEA = NewData
End If
End Sub
So depending on what calls this form - the NotInList or the Edit, I put it in the openargs parameter that calls frmBEA_JDIR. This is how I handle the update in the SAVE button. Again, the edit part works perfectly, but the AddNew from the NotInList event just won't populate cboBEA even after it is requeried and I can see the new value in it.
In brief: Instead of figuring out what to do in the popup with openargs, let acFormAdd do it for you; acFormAdd will open the form to a new record.
Send the new data in openargs.
Open frmBEA_JDIR in dialog mode. It stops the current code until the opened form is closed.
' open frmBEA_JDIR in dialog mode to add the new data.
DoCmd.OpenForm "frmBEA_JDIR", acNormal, , , acFormAdd, acDialog, "NewData"
'data is added, now requery the dropdown
cboBEA.Requery
Fool around with this without the 'case edit' section until you see it working. Then add the edit part using acFormEdit. You can check the datamode of the popped-up form to see if it's add or edit.
So this code is meant to serve as a simple search system to go to certain records in a recordset. I originally had it so they had to click the btnGoToID button in order to perform the search. I decided to just make it a little more user friendly and make it so the search field listened for the Enter button and that would perform the search as well.
The issue that I am running into when the code gets to strID = Trim(Nz(Me.txtSearch.Value, "")) the value will randomly come back as an empty string even though visually I can see that there is a value in the textbox.
I haven't been able to narrow down any pattern for when this issue occurs. At this point I don't really even know how to troubleshoot this problem, nor the words to search for that could yield any results in Google. All I can say is that it SEEMS like changing between records will affect whether or not the search goes through.
This has always worked up until I put in the txtSearch_KeyPress sub procedure.
'============================================================================
' txtSearch_KeyPress
'============================================================================
Private Sub txtSearch_KeyPress(KeyAscii As Integer)
'If user pressed enter
If KeyAscii = 13 Then
Call btnGoToID_Click
End If
End Sub
'============================================================================
' btnGoToID_Click
'============================================================================
' <<Purpose>>
' Allow the user to search for a specific ID
'============================================================================
Private Sub btnGoToID_Click()
On Error GoTo Err_Handler
Dim rs As Recordset
Dim strID As String
Set rs = Me.RecordsetClone
strID = Trim(Nz(Me.txtSearch.Value, ""))
If (strID <> "") Then
'Go to the ID
rs.FindFirst "ID = '" & strID & "'"
If rs.NoMatch Then
MsgBox "ID does not exist"
Else
'If we have a match, set the record as the current record
Me.Bookmark = rs.Bookmark
End If
Else
MsgBox "Please enter a valid ID.", vbOKOnly, "Invalid ID"
End If
Exit_Handler:
On Error Resume Next
Me.txtSearch.Value = ""
rs.Close
Set rs = Nothing
Exit Sub
Err_Handler:
Call LogError(Err.Number, Err.Description, "txtSearch on " & Me.Name)
Resume Exit_Handler
End Sub
After the conversation in the comments I have narrowed down this question to be a whole lot simpler. This yields an "Invalid use of null" error message even after there are several characters in the text field. Why would that happen, and what can I do to make it pick up the values in the textbox?
'============================================================================
' txtUnitNoToSearch_KeyPress
'============================================================================
Private Sub txtUnitNoToSearch_KeyPress(KeyAscii As Integer)
MsgBox Me.txtUnitNoToSearch
End Sub
I think your problem is related to the fact that a text box has 2 properties, .Value and .Text, which appear similar but behave differently.
While you have an edit in progress in the text box, the changing content is available via the .Text property. However, the content of the .Value property has not yet been updated to match.
After the text box's After Update event, .Value will contain the new content. And if focus has moved away from the text box, its .Text property will no longer even be available.
Sorry, I couldn't think how to explain that better. But I think the situation will be clearer with this KeyPress event procedure:
Private Sub txtUnitNoToSearch_KeyPress(KeyAscii As Integer)
Debug.Print "Text: '" & Me.txtUnitNoToSearch.Text & "'"
Debug.Print "Value: '" & Me.txtUnitNoToSearch.Value & "'"
End Sub
Watch the output from Debug.Print in the Immediate window as you make changes to the context of the text box. (Ctrl+g will open the Immediate window.)
One final point is that just Me.txtUnitNoToSearch gets you its default property which is .Value. Keep that in mind as you work through the rest of your code.
I have put the VBA in Unload Form Event in ms access 2010
Private Sub Form_Unload(Cancel As Integer)
Dim strMsg As String
Dim iResponse As Integer
' Specify the message to display.
strMsg = "Do you wish to save the changes?" & Chr(10)
strMsg = strMsg & "Click Yes to Save or No to Discard changes."
' Display the message box.
iResponse = MsgBox(strMsg, vbQuestion + vbYesNoCancel, "Save Record?")
' Check the user's response.
If iResponse = vbYes Then
' Undo the change.
DoCmd.RunCommand acCmdSave
End If
If iResponse = vbNo Then
' Undo the change.
DoCmd.RunCommand acCmdUndo
End If
If iResponse = vbCancel Then
' Undo the change
Cancel = True
End If
End Sub
If data is changed then the above code is working fine, then yes to save & close, No to undo & close and cancel to cancel event and remain on the form
but when the data is unchanged then Yes button is working fine, but NO button do not close the form
Where I m mistaking ?
Your question is too vague because you simply state "NO button do not close the form."
You can do:
DoCmd.Close
See here:
Private Sub cmdCloseForm_Click()
On Error GoTo Err_cmdCloseForm_Click
DoCmd.RunCommand acCmdUndo 'OR Me.Undo - test which works best for your situation
DoCmd.Close
Exit_cmdCloseForm_Click:
Exit Sub
Err_cmdCloseForm_Click:
MsgBox Err.Description
Resume Exit_cmdCloseForm_Click
End Sub
On Me.Undo from Allen Browne:
Me.Undo cancels the edits in a specific form, so that it is no longer dirty.
Once the form is no longer dirty, no further undo is possible, because it
has reached the desired state. However, Me. represents the form that is in focus, so your form must have focus to perform the Me.Undo command.
When a button is pressed I want to open a form at a specific tab.
on click event I have:
DoCmd.OpenForm "MAIN_USER_FORM", acNormal, , "App_Ref = " & Me.App_Ref, , , "PRB"
On the open event of the form I have:
If Me.OpenArgs = "PRB" Then
Me.PRB_Validation.SetFocus
End If
PRB_Validation is the name of the tab in the MAIN_USER_FORM I wish to open.
I've searched forms, I just can't get it to work any help would be great.
Thanks in advance.
All you need is to check the OpenArgs in the form's OnLoad event, and set the TabCtontrol's value to the index of the page you want to show, like this:
Private Sub Form_Load()
If OpenArgs = "PRB" Then
TabCtl0.Value = PagePRB.PageIndex
End If
End Sub
I made an example accdb to show the complete setup.
In case if anyone is looking for code where you have a button on another form and want to open Main form from that and also take user to a particular Tab.
Private Sub YourButton_Click()
On Error GoTo Err_YourButton_Click
Dim stDocName As String
Dim stLinkCriteria As String
stDocName = "YourFormName"
DoCmd.OpenForm stDocName, , , stLinkCriteria
Forms![YourFormName]!YourPage.SetFocus
Exit_YourButton_Click:
Exit Sub
Err_YourButton_Click:
MsgBox Err.Description
Resume Exit_YourButton_Click
End Sub