在父表单中过滤 Access 子表单的多个实例
Filtering multiple instances of Access subform in parent form
好的伙计,我很茫然。似乎有几种过滤子表单的方法,但我成功的方法很少,而且 none 已经能够处理多个实例。
这就是我想要完成的事情(这是我的 VBA 书呆子与我的 Fantasy Football 书呆子发生冲突的时候)。
我有一个 Fantasy Football Auction Draft 数据库,它从 FantasyPros 中提取使用以及球员统计数据、排名、价值等。
主要形式允许我实时跟踪草稿。当球员被购买时,交易会被记录下来并与经理相关联。
数据库的"Meat"是frm_ManagerBox。此表单 Screen shot here 正在使用大约 16 个查询提取数据,但主要记录源是经理 table。
frm_ManagerBox 作为一个独立的表格工作得很好,我可以毫无问题地滚动浏览记录(我们联盟有 12 位经理)。
我的目标是拥有一个包含 12 个 frm_ManagerBox 实例的主父表单 (frm_ProfileHub),每个实例都过滤到我们 12 位联盟经理中的一位。理想情况下,我有一个包含 12 个框的大表单,在每个框下方有一个组合框,用于将子表单填充到适当的管理器。
但是,当我尝试嵌入子表单时,一切都变得一团糟。子表单的查询全部中断(在表单加载时,我得到 16 个弹出窗口,询问每个源的定义)。
我的理解是,子表单中的查询必须调用父表单中的数据。我可以手动设置子窗体的每个查询的条件以从父窗体的组合框中提取,但这不会说明我在父窗体上需要的其他 11 个实例。
如有任何帮助或指导,我们将不胜感激。选秀日即将到来!
谢谢!
Link场预赛
Link 主字段和 Link 子字段(Link 字段)定义子表单记录源和父记录之间的基本绑定。换句话说,如果子表单应该在父表单导航到新记录时自动更新以仅显示相关记录,则 Link 字段指定此过滤。当父项和子项具有明确定义的主键和外键对时,这种内置的子表单绑定工作得非常好,没有任何麻烦。在规范化良好的数据库中,密钥对通常是单字段数字 ID。一个例子是:
Link Master Fields: ID
Link Child Fields: ParentID
但是,通过多个字段关联父查询和子查询是完全合理的。 Link 字段也支持这一点。多个字段名以分号分隔。再举个例子:
Link Master Fields: Title;TrxDate
Link Child Fields: GroupTitle;TrxDate
子表单也可以独立于父表单的记录源,或者父表单甚至可能没有记录源——充当未绑定元素和子表单的静态容器。换句话说,不需要根据父记录导航自动过滤子表单。在这种情况下,Link 字段设置为空字符串。
在 运行 时间设置 RecordSource 属性
虽然表单记录源通常在设计时指定,但这不是必需的。有时 and/or 需要在各种情况下 运行 期间设置记录源,例如当
- 应根据未绑定的控件值过滤子表单。
- 子表单旨在显示来自相似(相同字段和数据类型)但不同查询或 tables 的记录。
- 同一个窗体将成为同一个父窗体上多个子窗体控件的源对象。
当子窗体 RecordSource 属性 在 运行 时设置时,最好随后立即设置 LinkMasterFields 和 LinkChildFields 属性。即使 Link 字段应为空白(即未定义)也是如此。这是因为如果 Access 确定父窗体的记录源和子窗体的记录源具有兼容的字段,它将自动定义 Link 字段。有时它会根据索引和关系猜测正确,但有时它会定义不需要的和伪造的 Link 字段,因此最好明确设置它们。
话虽如此,设置子表单记录源的方法和是否指定Link字段是真正分开的问题。任何一个方面的方法都是通过满足不同的要求来定义的。
例子
何时何地设置子窗体的 RecordSource 属性 取决于应如何根据父窗体的其他因素过滤子窗体。可能有共同的模式,但除此之外没有特别的要求或确定的方法。仅作为示例,我将提及几种可能性。
如果在加载父表单时已知所有变量,则在 Form_Load() 事件处理程序中设置子表单的 RecordSource 可能就足够了。例如,假设您有一个用于管理音乐播放列表的父表单。您想要分别显示 public 和私人播放列表,但它们在其他方面具有相同的 attributes/fields。您创建一个包含所有播放列表字段的表单,然后将两个子表单控件添加到主表单并将每个源对象设置为相同的播放列表表单。现在您在父窗体上定义以下内容:
'* ---------------------------------------------------
'* 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
等完整引用进行编码,但这与更好的编程实践背道而驰。
现在考虑一个更复杂和相关的例子。我只是在编造细节,因为问题没有具体说明。在这种情况下,父表单表示特定用户的记录。对于每个用户,多个子表单显示不同 "managers" 的统计信息。只为子窗体创建一个窗体,因此需要在 运行 时间内为多个子窗体控件实例设置 RecordSource 属性。
如果多个 "managers" 是预定义的——可能存储在可以在 RecordSource 查询中连接的数据库 table 中,添加适当的连接会相当简单 and/or具体查询中的引用。但我们会更进一步,让管理员为每个用户动态选择。因此,每个子表单都会有一个相应的组合框,用于从可用的管理器中进行选择。
老实说,这可以通过与上一个示例几乎相同的方式来完成。这是父窗体上 Form_Load 事件处理程序的快速粗略显示。它使用两个功能来过滤每个子表单:1) Link 字段和 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 和管理员 table 之间也有一个明显的 link。这些都不一定是这种情况,因此代码可能看起来像
'* 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
我发现在我自己的许多复杂表单中,内置的自动绑定功能和表单控件引用——虽然它们编程起来更快——但在导航过程中可能会相当慢。多个子表单很可能已经减慢了父表单的速度,但是如果定义了 Link 字段并且查询包含对表单控件的直接引用,则表单上的每个琐碎操作都可能会过度刷新。这是一种替代方法,它仅在相关值更新时将每个 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
对于最初的发布者来说可能有点晚了,但这里有一个不需要 VBA 并且非常容易设置的解决方案。通过在此处定义过滤器,使用 Link Master/Child 字段对多个字段 link 的能力对您有利。
一个具体的例子。假设您正在跟踪一个人的行动项目。您有一个名为 frmPerson 的父表单和一个名为 frmActionItems 的子表单,它们 link 在 PersonId 上。要建立这种父子关系,您需要使用:
Link Master Fields: personId
Link Child Fields: personId
现在假设您决定需要两个 frmActionItems 子表单 - 一个显示所有当前活动的操作项,一个显示所有已完成的操作项。操作项 table 上有一个必需的 isCompleted Yes/No 字段。
首先,在 frmPerson 表单中添加两个不可见的文本框 - 我们称它们为 txtIsActive 和 txtIsCompleted。这两个文本框应该是未绑定的,但将 txtIsActive 的默认值设置为 =False
并将 txtIsCompleted 的默认值设置为 =True
.
用于显示活动操作项的子表单将link编辑如下:
Link Master Fields: personId;txtIsActive
Link Child Fields: personId;isCompleted
...并且因为 txtIsActive 是 false
,子表单将只显示 isCompleted 值为 false
的记录,所以那些是活动的。同样,我们将用于显示已完成操作项的子表单将 link 像这样编辑:
Link Master Fields: personId;txtIsCompleted
Link Child Fields: personId;isCompleted
最初的发布者可以为他的场景设置类似的东西,但有更多隐藏的、未绑定的控件。
好的伙计,我很茫然。似乎有几种过滤子表单的方法,但我成功的方法很少,而且 none 已经能够处理多个实例。
这就是我想要完成的事情(这是我的 VBA 书呆子与我的 Fantasy Football 书呆子发生冲突的时候)。
我有一个 Fantasy Football Auction Draft 数据库,它从 FantasyPros 中提取使用以及球员统计数据、排名、价值等。
主要形式允许我实时跟踪草稿。当球员被购买时,交易会被记录下来并与经理相关联。
数据库的"Meat"是frm_ManagerBox。此表单 Screen shot here 正在使用大约 16 个查询提取数据,但主要记录源是经理 table。
frm_ManagerBox 作为一个独立的表格工作得很好,我可以毫无问题地滚动浏览记录(我们联盟有 12 位经理)。
我的目标是拥有一个包含 12 个 frm_ManagerBox 实例的主父表单 (frm_ProfileHub),每个实例都过滤到我们 12 位联盟经理中的一位。理想情况下,我有一个包含 12 个框的大表单,在每个框下方有一个组合框,用于将子表单填充到适当的管理器。
但是,当我尝试嵌入子表单时,一切都变得一团糟。子表单的查询全部中断(在表单加载时,我得到 16 个弹出窗口,询问每个源的定义)。
我的理解是,子表单中的查询必须调用父表单中的数据。我可以手动设置子窗体的每个查询的条件以从父窗体的组合框中提取,但这不会说明我在父窗体上需要的其他 11 个实例。
如有任何帮助或指导,我们将不胜感激。选秀日即将到来! 谢谢!
Link场预赛
Link 主字段和 Link 子字段(Link 字段)定义子表单记录源和父记录之间的基本绑定。换句话说,如果子表单应该在父表单导航到新记录时自动更新以仅显示相关记录,则 Link 字段指定此过滤。当父项和子项具有明确定义的主键和外键对时,这种内置的子表单绑定工作得非常好,没有任何麻烦。在规范化良好的数据库中,密钥对通常是单字段数字 ID。一个例子是:
Link Master Fields: ID
Link Child Fields: ParentID
但是,通过多个字段关联父查询和子查询是完全合理的。 Link 字段也支持这一点。多个字段名以分号分隔。再举个例子:
Link Master Fields: Title;TrxDate
Link Child Fields: GroupTitle;TrxDate
子表单也可以独立于父表单的记录源,或者父表单甚至可能没有记录源——充当未绑定元素和子表单的静态容器。换句话说,不需要根据父记录导航自动过滤子表单。在这种情况下,Link 字段设置为空字符串。
在 运行 时间设置 RecordSource 属性
虽然表单记录源通常在设计时指定,但这不是必需的。有时 and/or 需要在各种情况下 运行 期间设置记录源,例如当
- 应根据未绑定的控件值过滤子表单。
- 子表单旨在显示来自相似(相同字段和数据类型)但不同查询或 tables 的记录。
- 同一个窗体将成为同一个父窗体上多个子窗体控件的源对象。
当子窗体 RecordSource 属性 在 运行 时设置时,最好随后立即设置 LinkMasterFields 和 LinkChildFields 属性。即使 Link 字段应为空白(即未定义)也是如此。这是因为如果 Access 确定父窗体的记录源和子窗体的记录源具有兼容的字段,它将自动定义 Link 字段。有时它会根据索引和关系猜测正确,但有时它会定义不需要的和伪造的 Link 字段,因此最好明确设置它们。
话虽如此,设置子表单记录源的方法和是否指定Link字段是真正分开的问题。任何一个方面的方法都是通过满足不同的要求来定义的。
例子
何时何地设置子窗体的 RecordSource 属性 取决于应如何根据父窗体的其他因素过滤子窗体。可能有共同的模式,但除此之外没有特别的要求或确定的方法。仅作为示例,我将提及几种可能性。
如果在加载父表单时已知所有变量,则在 Form_Load() 事件处理程序中设置子表单的 RecordSource 可能就足够了。例如,假设您有一个用于管理音乐播放列表的父表单。您想要分别显示 public 和私人播放列表,但它们在其他方面具有相同的 attributes/fields。您创建一个包含所有播放列表字段的表单,然后将两个子表单控件添加到主表单并将每个源对象设置为相同的播放列表表单。现在您在父窗体上定义以下内容:
'* ---------------------------------------------------
'* 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
等完整引用进行编码,但这与更好的编程实践背道而驰。
现在考虑一个更复杂和相关的例子。我只是在编造细节,因为问题没有具体说明。在这种情况下,父表单表示特定用户的记录。对于每个用户,多个子表单显示不同 "managers" 的统计信息。只为子窗体创建一个窗体,因此需要在 运行 时间内为多个子窗体控件实例设置 RecordSource 属性。
如果多个 "managers" 是预定义的——可能存储在可以在 RecordSource 查询中连接的数据库 table 中,添加适当的连接会相当简单 and/or具体查询中的引用。但我们会更进一步,让管理员为每个用户动态选择。因此,每个子表单都会有一个相应的组合框,用于从可用的管理器中进行选择。
老实说,这可以通过与上一个示例几乎相同的方式来完成。这是父窗体上 Form_Load 事件处理程序的快速粗略显示。它使用两个功能来过滤每个子表单:1) Link 字段和 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 和管理员 table 之间也有一个明显的 link。这些都不一定是这种情况,因此代码可能看起来像
'* 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
我发现在我自己的许多复杂表单中,内置的自动绑定功能和表单控件引用——虽然它们编程起来更快——但在导航过程中可能会相当慢。多个子表单很可能已经减慢了父表单的速度,但是如果定义了 Link 字段并且查询包含对表单控件的直接引用,则表单上的每个琐碎操作都可能会过度刷新。这是一种替代方法,它仅在相关值更新时将每个 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
对于最初的发布者来说可能有点晚了,但这里有一个不需要 VBA 并且非常容易设置的解决方案。通过在此处定义过滤器,使用 Link Master/Child 字段对多个字段 link 的能力对您有利。
一个具体的例子。假设您正在跟踪一个人的行动项目。您有一个名为 frmPerson 的父表单和一个名为 frmActionItems 的子表单,它们 link 在 PersonId 上。要建立这种父子关系,您需要使用:
Link Master Fields: personId
Link Child Fields: personId
现在假设您决定需要两个 frmActionItems 子表单 - 一个显示所有当前活动的操作项,一个显示所有已完成的操作项。操作项 table 上有一个必需的 isCompleted Yes/No 字段。
首先,在 frmPerson 表单中添加两个不可见的文本框 - 我们称它们为 txtIsActive 和 txtIsCompleted。这两个文本框应该是未绑定的,但将 txtIsActive 的默认值设置为 =False
并将 txtIsCompleted 的默认值设置为 =True
.
用于显示活动操作项的子表单将link编辑如下:
Link Master Fields: personId;txtIsActive
Link Child Fields: personId;isCompleted
...并且因为 txtIsActive 是 false
,子表单将只显示 isCompleted 值为 false
的记录,所以那些是活动的。同样,我们将用于显示已完成操作项的子表单将 link 像这样编辑:
Link Master Fields: personId;txtIsCompleted
Link Child Fields: personId;isCompleted
最初的发布者可以为他的场景设置类似的东西,但有更多隐藏的、未绑定的控件。