好吧,伙计们,我不知所措。似乎有几种方法可以过滤子表单,但我成功地使用了其中很少的方法,而且没有一种能够处理多个实例。
以下是我正在努力实现的目标(这是我的VBA书呆子与我的梦幻足球书呆子发生冲突的时候)。
我有一个Fantasy Football Auction Draft数据库,它从FantasyPros中提取使用情况以及球员统计数据、排名、价值等。
主窗体允许我实时跟踪草稿。当玩家被购买时,交易会被记录下来并与经理联系起来。
数据库的"肉"是frm_ManagerBox。这里的表单屏幕截图提取了大约16个查询的数据,但主要记录源是Manager表。
frm_ManagerBox作为一个独立的表单运行良好,我可以毫无问题地滚动记录(我们联盟有12名经理)。
我的目标是拥有一个主父窗体(frm_ProfileHub),它有12个frm_ManagerBox实例,每个实例都过滤到我们的12个联盟经理中的一个。理想情况下,我会有一个包含12个框的大表单,在每个框下都有一个组合框,用于将子表单填充到适当的管理器中。
然而,一旦我试图嵌入子表单,一切都会变得一团糟。子表单的查询都中断了(在表单加载时,我会收到16个弹出窗口,询问每个源的定义)。
我的理解是,子窗体中的查询必须调用父窗体中的数据。我可以手动设置子窗体的每个查询的条件,以从父窗体的组合框中提取,但这无法解释我在父窗体上需要的其他11个实例。
如有任何帮助或指导,我们将不胜感激。征兵日即将到来!谢谢
对于最初的海报来说可能有点晚了,但这里有一个不需要VBA并且很容易设置的解决方案。使用链接主/子字段的功能,通过在此处定义过滤器,可以针对多个字段进行链接,以达到您的优势。
一个具体的例子。假设您正在跟踪一个人的动作项目。您有一个名为frmPerson的父窗体和一个称为frmActionItems的子窗体,它们在PersonId上链接。要建立这种父子关系,您可以使用:
Link Master Fields: personId
Link Child Fields: personId
现在,假设您决定要两个frmActionItems子窗体——一个显示当前所有活动的动作项,另一个显示所有已完成的动作项。行动项目表上有一个必需的isCompleted是/否字段。
首先,将两个不可见的文本框添加到frmPersen表单中——让我们称它们为txtIsActive和txtIsCompleted。这两个文本框应解除绑定,但将txtIsActive的默认值设置为=False
,将txtIsCompleted的默认值设为=True
。
用于显示活动动作项目的子窗体将链接如下:
Link Master Fields: personId;txtIsActive
Link Child Fields: personId;isCompleted
并且因为txtIsActive是false
,所以子窗体将只显示isCompleted值为false
的记录,所以那些是活动的。类似地,我们将用于显示已完成动作项目的子窗体将链接如下:
Link Master Fields: personId;txtIsCompleted
Link Child Fields: personId;isCompleted
最初的海报本可以为他的场景设置类似的东西,但有更多隐藏的、未绑定的控件。
链接字段预备
链接主字段和链接子字段(链接字段)定义子窗体记录源和父记录之间的基本绑定。换句话说,如果在父窗体导航到新记录时,子窗体应自动更新以仅显示相关记录,则"链接"字段将指定此筛选。当父级和子级具有定义良好的主键和外键对时,这种内置的子窗体绑定工作得非常好,没有任何麻烦。在一个规范化良好的数据库中,密钥对通常是单个字段的数字ID。例如:
Link Master Fields: ID
Link Child Fields: ParentID
但是,父查询和子查询通过多个字段进行关联是完全合理的。链接字段也支持这一点。多个字段名用分号分隔。另一个例子:
Link Master Fields: Title;TrxDate
Link Child Fields: GroupTitle;TrxDate
子窗体也可以独立于父窗体的记录源,或者父窗体甚至可能没有记录源——充当未绑定元素和子窗体的静态容器。换句话说,子表单不需要基于父记录导航进行自动筛选。在这种情况下,"链接"字段设置为空字符串。
在运行时设置RecordSource属性
尽管表单记录源通常在设计时指定,但这不是一个要求。在各种情况下,有时在运行时设置记录源是有用的和/或必要的,例如
- 子窗体应该根据未绑定的控件值进行筛选
- 子窗体设计用于显示来自相似(相同字段和数据类型)但不同查询或表的记录
- 同一窗体将是同一父窗体上多个子窗体控件的源对象
在运行时设置子窗体RecordSource属性时,最好在之后立即设置LinkMasterFields和LinkChildFields属性。即使链接字段应为空(即未定义),也是如此。这是因为如果Access确定父窗体的记录源和子窗体的记录来源具有兼容的字段,它将自动定义"链接"字段。有时它会根据索引和关系进行猜测,但有时它会定义不需要的和伪造的链接字段,所以最好显式设置它们。
话虽如此,设置子窗体记录源的方法和是否指定了链接字段实际上是单独的问题。任何一个方面的方法都是通过满足不同的需求来定义的。
示例
何时何地设置子窗体的RecordSource属性取决于如何根据父窗体的其他因素筛选子窗体。可能有常见的模式,但除此之外没有特定的要求或明确的方法。作为一个例子,我只会提到几个可能性。
如果在加载父窗体时所有变量都是已知的,那么在form_Load()事件处理程序中设置子窗体的RecordSource就足够了。例如,考虑您有一个用于管理音乐播放列表的父窗体。您希望分别显示公共和私人播放列表,但它们在其他方面具有相同的属性/字段。您创建一个包含所有播放列表字段的窗体,然后在主窗体中添加两个子窗体控件,并将每个源对象设置为同一播放列表窗体。现在您在父窗体上定义以下内容:
'* ---------------------------------------------------
'* In the subform's module
Private Sub SetRecordSource(visibility as String)
Me.RecordSource = "SELECT * FROM PlayLists WHERE [Visbility] = '" & visibility & "'"
End Sub
Public Sub SetPublicRecordSource()
SetRecordSource "Public"
End Sub
Public Sub SetPrivateRecordSource()
SetRecordSource "Private"
End Sub
'* ---------------------------------------------------
'* In the parent form's module
Private Sub Form_Load()
'* I personally like using strongly-typed local variables
'* to enhance compile-time error checking and to facilitate Intellisense
Dim subform as Form_Playlist
With Me.PrivatePlaylistSubform
Set subform = .Form
subform.SetPrivateRecordSource
.LinkMasterFields = "ID"
.LinkChildFields = "PersonID"
End With
With Me.PublicPlaylistSubform
Set subform = .Form
subform.SetPublicRecordSource
.LinkMasterFields = "ID"
.LinkChildFields = "PersonID"
End With
End Sub
这一切本可以在Form_Load()事件中使用像Me.PrivatePlaylistSubform.Form.RecordSource
这样的完全引用进行编码,但这违背了更好的编程实践。
现在考虑一个更复杂和相关的例子。我只是在编细节,因为问题没有具体说明。在这种情况下,父窗体表示特定用户的记录。对于每个用户,多个子窗体显示不同"经理"的统计信息。子窗体只创建一个窗体,因此需要在运行时为多个子窗体控件实例设置RecordSource属性。
如果多个"管理器"是预定义的——可能存储在一个可以在RecordSource查询中联接的数据库表中,那么在特定查询中添加适当的联接和/或引用将非常简单。但我们将更进一步,让每个用户动态地选择经理。因此,每个子窗体都将有一个相应的组合框,用于从可用的管理器中进行选择。
老实说,这可以用与上一个例子几乎相同的方式来实现。这是父窗体上Form_Load事件处理程序的快速而肮脏的展示。它使用两个功能来过滤每个子窗体:1)链接字段and 2)引用父窗体上的特定控件。
Private Sub Form_Load()
'* ManagerBox# are all subform controls
'* comboManagers# are ComboBoxes corresponding to each subform
'* Both the parent form and each child form's record source has a UserID field.
'* frm_ProfileHub is the name of the parent form.
With Me.ManagerBox1
.Form.RecordSource = "SELECT * FROM UserManagers" & _
" WHERE ManagerID = [Forms]![frm_ProfileHub]![comboManager1]"
.LinkMasterFields = "UserID"
.LinkChildFields = "UserID"
End With
With Me.ManagerBox2
.Form.RecordSource = "SELECT * FROM UserManagers" & _
" WHERE ManagerID = [Forms]![frm_ProfileHub]![comboManager2]"
.LinkMasterFields = "UserID"
.LinkChildFields = "UserID"
End With
End Sub
在上面的代码中,我假设ComboBox控件没有绑定到主记录的字段。主用户ID和管理器表之间还有一个明显的链接。这两者都不是必须的,因此代码可能看起来像
'* Subform is not directly linked to the primary record
'* Subform is only filtered by the ComboBox value
With Me.ManagerBox2
.Form.RecordSource = "SELECT * FROM Managers" & _
" WHERE ManagerID = [Forms]![frm_ProfileHub]![comboManager2]"
.LinkMasterFields = ""
.LinkChildFields = ""
End With
或
'* In this case, the comboBoxes are already bound to a parent form's field,
'* more specifically to fields labeled [MangerID1], [MangerID2], etc.
'* So instead of referring to a form control (which is less efficient),
'* just use the Link fields to handle the binding.
'* RecordSource is actually identical, so that could be set
'* at design time and then only update the Link fields at run-time.
With Me.ManagerBox2
.Form.RecordSource = "SELECT * FROM UserManagers WHERE"
.LinkMasterFields = "UserID;ManagerID2"
.LinkChildFields = "UserID;ManagerID"
End With
我在自己的许多复杂表单中发现,内置的自动绑定功能和表单控件引用虽然编程更快,但在导航过程中可能会相当慢。多个子表单可能已经降低了父表单的速度,但如果定义了链接字段,并且查询包含对表单控件的直接引用,则表单上的每一个三角操作都可能会有过多的刷新。以下是一种替代方法,仅当更新相关值时,才将每个RecordSource设置为特定于验证的过滤查询。
'* ---------------------------------------------------
'* In the subform's module
Public Sub SetRecordSource(vUserID As Variant, vManagerID as variant)
If Not (IsNumeric(vUserID) AND IsNumeric(vManagerID)) Then
Me.RecordSource = ""
Else
Me.RecordSource = "SELECT * FROM UserManagers" & _
" WHERE UserID = " & vUserID & " AND ManagerID = " & vManagerID
End If
End Sub
'* ---------------------------------------------------
'* In the parent form's module
Private Sub LinkManagerBox1()
Dim subform as Form_frm_ManagerBox
With Me.ManagerBox1
Set subform = .Form
subform.SetRecordSource Me.UserID, comboManager1.Value
.LinkMasterFields = ""
.LinkChildFields = ""
End With
End Sub
Private Sub LinkManagerBox2()
Dim subform as Form_frm_ManagerBox
With Me.ManagerBox2
Set subform = .Form
subform.SetRecordSource Me.UserID, comboManager2.Value
.LinkMasterFields = ""
.LinkChildFields = ""
End With
End Sub
Private Sub comboManager1_AfterUpdate()
LinkManagerBox1
End Sub
Private Sub comboManager2_AfterUpdate()
LinkManagerBox2
End Sub
Private Sub Form_Current()
LinkManagerBox1
LinkManagerBox2
End Sub