0

好吧,伙计,我不知所措。似乎有几种过滤子表单的方法,但我成功使用的方法很少,而且没有一个能够处理多个实例。

这就是我想要完成的事情(这是我的 VBA 书呆子与我的 Fantasy Football 书呆子相冲突的时候)。

我有一个 Fantasy Football Auction Draft 数据库,它从 FantasyPros 中提取使用情况以及球员统计数据、排名、价值等。

主要表格允许我实时跟踪草稿。随着球员的购买,交易被记录下来并与经理联系起来。

数据库的“肉类”是 frm_ManagerBox。此处的此表单屏幕截图正在使用大约 16 个查询提取数据,但主要记录源是 Manager 表。

frm_ManagerBox 作为一个独立的表单运行良好,我可以毫无问题地滚动浏览记录(我们联盟中有 12 位经理)。

我的目标是拥有一个包含 12 个 frm_ManagerBox 实例的主父表单 (frm_ProfileHub),每个实例都过滤到我们 12 位联盟经理中的一位。理想情况下,我应该有一个包含 12 个框的大表单,每个框下都有一个组合框,用于将子表单填充到相应的管理器。

但是,一旦我尝试嵌入子表单,这一切都变成了废话。子表单的查询全部中断(在表单加载时,我收到 16 个弹出窗口,询问每个源的定义)。

我的理解是子表单中的查询必须从父表单中调用数据。我可以手动设置子表单的每个查询的条件,以从父表单的组合框中提取,但这并不能说明我在父表单上需要的 11 个其他实例。

任何帮助或指导将不胜感激。选秀日即将到来!谢谢!

4

2 回答 2

0

链接字段预备

链接主字段和链接子字段(链接字段)定义子表单记录源和父记录之间的基本绑定。换句话说,如果子表单应该在父表单导航到新记录时自动更新以仅显示相关记录,则链接字段指定此过滤。当父项和子项具有明确定义的主键和外键对时,这种内置的子表单绑定可以很好地工作。在规范化的数据库中,密钥对通常是单字段数字 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
于 2017-08-23T07:02:26.817 回答
0

也许原始海报有点晚了,但这是一个不需要 VBA 并且很容易设置的解决方案。通过在此处定义过滤器,使用 Link Master/Child 字段链接多个字段的能力对您有利。

一个具体的例子。假设您正在跟踪一个人的操作项。您有一个名为 frmPerson 的父表单和一个名为 frmActionItems 的子表单,它们链接到 PersonId。要建立这种父子关系,您可以使用:

Link Master Fields:  personId
Link Child Fields:   personId

假设现在,您决定需要两个 frmActionItems 子表单 - 一个显示所有当前活动的操作项,一个显示所有已完成的操作项。操作项表上有一个必需的 isCompleted Yes/No 字段。

首先,在 frmPerson 表单中添加两个不可见的文本框 - 我们称它们为 txtIsActive 和 txtIsCompleted。这两个文本框应该是未绑定的,但是将 txtIsActive 的默认值设置为 txtIsCompleted=False的默认值=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

最初的海报本可以为他的场景设置类似的东西,但有更多隐藏的、未绑定的控件。

于 2018-01-12T16:03:25.897 回答