runtime error 2448 you cant assign a value to this

2019-02-19 06:33发布

问题:

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.

回答1:

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