链接字段预备
链接主字段和链接子字段(链接字段)定义子表单记录源和父记录之间的基本绑定。换句话说,如果子表单应该在父表单导航到新记录时自动更新以仅显示相关记录,则链接字段指定此过滤。当父项和子项具有明确定义的主键和外键对时,这种内置的子表单绑定可以很好地工作。在规范化的数据库中,密钥对通常是单字段数字 ID。一个例子是:
Link Master Fields: ID
Link Child Fields: ParentID
但是,通过多个字段关联父查询和子查询是完全合理的。链接字段也支持这一点。多个字段名用分号分隔。再举一个例子:
Link Master Fields: Title;TrxDate
Link Child Fields: GroupTitle;TrxDate
子表单也可以独立于父表单的记录源,或者父表单甚至可能没有记录源——充当未绑定元素和子表单的静态容器。换句话说,不需要根据父记录导航自动过滤子表单。在这种情况下,链接字段设置为空字符串。
在运行时设置 RecordSource 属性
尽管通常在设计时指定表单记录源,但这不是必需的。在各种情况下,有时在运行时设置记录源是有用和/或必要的,例如当
- 应根据未绑定的控制值过滤子表单。
- 该子表单旨在显示来自相似(相同字段和数据类型)但不同的查询或表的记录。
- 同一个窗体将是同一个父窗体上多个子窗体控件的源对象。
在运行时设置子表单 RecordSource 属性时,最好在之后立即设置 LinkMasterFields 和 LinkChildFields 属性。即使链接字段应为空白(即未定义)也是如此。这是因为如果 Access 确定父表单的记录源和子表单的记录源具有兼容的字段,它将自动定义链接字段。有时它会根据索引和关系猜测正确,但有时它会定义不需要的和虚假的 Link 字段,因此最好明确设置它们。
话虽如此,设置子表单记录源的方法和是否指定链接字段确实是单独的问题。通过满足不同的要求来定义任一方面的方法。
例子
何时何地设置子表单的 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 查询中连接的数据库表中,那么在特定查询中添加适当的连接和/或引用将相当简单。但我们将更进一步,让管理人员为每个用户动态选择。因此,每个子表单都有一个相应的 ComboBox 用于从可用的管理器中进行选择。
老实说,这可以以与上一个示例几乎相同的方式完成。这是父窗体上 Form_Load 事件处理程序的快速而肮脏的展示。它使用两个功能来过滤每个子窗体:1) 链接字段和 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 控件未绑定到主记录的字段。主 userID 和 manager 表之间也有明显的联系。这些都不是这种情况,因此代码可能看起来像
'* 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