哪个最好取决于许多因素。首先,如果你想在没有参数的情况下运行报表,你不想在报表的记录源中定义它们。这也是您的第一个建议的问题,它将报告与表单联系起来(从 Access/Jet 的角度来看,在记录源的 SQL 中声明的 PARAMETER 和表单控件引用之间几乎没有区别;实际上,如果你做得对,任何时候你使用表单控件引用,你都会将它定义为一个参数!)。
在这三个中,最灵活的是您的第二个建议,这意味着无需打开表单,也无需在运行时提供参数即可运行报表。
您忽略了一种介于两者之间的可能性,那就是使用表单的 OnOpen 事件在运行时设置记录源。为此,您将使用 acDialog 参数打开要收集日期的表单,在填写完表单后将其隐藏,然后即时编写记录源。像这样的:
Private Sub Report_Open(Cancel As Integer)
Dim dteStart as Date
Dim dteEnd As Date
DoCmd.OpenForm "dlgGetDates", , , , , acDialog
If IsLoaded("dlgGetDates") Then
With Forms!dlgGetDates
dteStart = !StartDate
dteEnd = !EndDate
End With
Me.Recordsource = "SELECT * FROM MyTable WHERE DateField Between #" _
& dteStart & "# AND #" & dteEnd & "#;"
DoCmd.Close acForm, "dlgGetDates"
End If
End Sub
[编辑:IsLoaded() 是 MS 提供的函数。它对我所有的 Access 编码都非常基础,我忘记了它不是内置函数。代码:]
Function IsLoaded(ByVal strFormName As String) As Boolean
' Returns True if the specified form is open in Form view or Datasheet view.
Const conObjStateClosed = 0
Const conDesignView = 0
If SysCmd(acSysCmdGetObjectState, acForm, strFormName) <> conObjStateClosed Then
If Forms(strFormName).CurrentView <> conDesignView Then
IsLoaded = True
End If
End If
End Function
现在,这种方法有一些优势:
您可以预先填写日期,这样用户只需单击“确定”即可。
您可以在多个位置重复使用该表单来收集多个表单的日期值,因为该表单并不关心它在哪个报告中使用。
但是,没有条件选择是否打开表单。
因此,我经常使用类模块来存储报告的过滤条件,然后检查相关的类模块实例是否在 OnOpen 事件触发时设置了过滤器值。如果设置了条件,那么我会即时为记录源编写 SQL,如果没有设置,我只使用默认记录源(以及在 OpenReport 参数中传递的任何过滤器)。在此设置中,您仅在收集条件并设置类模块实例后才运行报告。这种方法的优点是报表可以在两种上下文中的任何一个中运行,并且任何部分都不需要了解彼此的任何信息——报表只需要了解类模块的接口即可。