同步访问屏幕行上的两个数据表

Synchronize Access two datasheets on screen rows

我使用子表单数据表模式显示table。如果我只使用一个子表单,table 将非常宽。我将把字段分成几组。每个组将由一个子窗体显示,每个子窗体将在选项卡控件的选项卡中。如何同步屏幕行上的每个子表单?例如,当第 15 行被选中时,用户滚动子表单 A 和第 12~23 行现在在屏幕上。我可以将屏幕上的其他子表单设置为第 12~23 行,同时也选择第 15 行吗?这意味着所有子表单显示行和选定行是同步的。

在主窗体上,放置一个文本框,例如 txtSyncSubforms。

对其应用此控件源:

=SyncSubforms([subControlFirst]![ID],[subControlSecond]![ID], .., [subControlLast]![ID])

用子表单控件的实际名称和 ID 替换 subControlxxxx 和 ID,当然,ID 必须是唯一的。

在表单后面添加此代码:

Option Compare Database
Option Explicit

' Automatic synchronizing of multiple subforms.
' 2019-01-05. Gustav Brock, Cactus Data ApS, CPH.
' Version 1.2.0
' License: MIT.

    ' Index for Split to separate the name of the subform control from
    ' the name of the control with the key.
    '   [subControlAny]![ID]
    ' will be split into:
    '   [subControlAny]
    ' and:
    '   [ID]
    Enum ControlName
    SubForm = 0
    Key = 1
    End Enum

Private Function SyncSubforms(ParamArray sControls() As Variant) As Variant

' Array sControls() holds the values of the key controls on the subform controls
' to be held in sync.

    ' Name of visible textbox on main form bound to this function.
    Const cControl  As String = "txtSyncSubforms"

    ' Static to store the value of the key of the last synced record.
    Static wLastID  As Variant

Dim rst         As DAO.Recordset
Dim wSubform    As Form

' Array to hold the names of the subform controls and key controls.
Dim aControls() As String

Dim bmk         As Variant
Dim wNew        As Boolean
Dim wThisID     As Variant
Dim wIndex      As Integer
Dim wItem       As Integer
Dim wCount      As Long
Dim wFieldName  As String

' If any key value is Null, we have moved to a new record.
' No syncing shall take place.
For wIndex = LBound(sControls()) To UBound(sControls())
    wThisID = sControls(wIndex).Value
    If IsNull(wThisID) Then
        If sControls(wItem).Parent.Name = Me.ActiveControl.SourceObject Then
            ' New record. Don't sync.
            wNew = True
            Exit For
        End If
     ElseIf IsNull(wLastID) Or Me.ActiveControl.Form.NewRecord Then
        ' Initial opening of form, or new record has been created.
        ' Set wLastID to the value of the current key of the first subform
        ' or to the key of the new record.
        wLastID = wThisID
        ' Stop further processing.
        wNew = True
        Exit For
ElseIf IsEmpty(wThisID) Then
        ' Record has been deleted.
        ' Pull the ID from the active subform.
        For wItem = LBound(sControls) To UBound(sControls)
          If sControls(wItem).Parent.Name = Me.ActiveControl.SourceObject Then
              wThisID = Me.ActiveControl(sControls(wItem).Name).Value
' Store as the current key.
wLastID = wThisID
Exit For
            End If
          Next
Exit For
        ElseIf wThisID <> wLastID Then
            ' This key is the new value to sync the other subforms to.
            ' Store the current key.
            wLastID = wThisID
            Exit For
          End If
        Next

If wNew = True Then
    ' New record or initial opening. Do nothing.
Else
    ' ControlSource of cControl will read like:
    '   =SyncSubforms([subControlFirst]![ID],[subControlSecond]![ID], .., [subControlLast]![ID])
    '
    ' Build array of the names of the subform controls with the key controls:
    '   [subControlFirst]![ID]
    '   [subControlSecond]![ID]
    '   ...
    '   [subControlAny]![ID]
    '   ...
    '   [subControlLast]![ID]
    ' by extracting arg names between "(" and ")".
    aControls = Split(Replace(Split(Me(cControl).ControlSource, "(")(1), ")", ""), ",")

    ' Get current record count as it will change after an append or delete in one of the subforms.
    For wIndex = LBound(aControls()) To UBound(aControls())
      If Me(Split(aControls(wIndex), "!")(ControlName.SubForm)).Name = Me.ActiveControl.Name Then
      Set wSubform = Me(Split(aControls(wIndex), "!")(ControlName.SubForm)).Form
      wCount = wSubform.RecordsetClone.RecordCount
      Exit For
    End If
Next

' Loop to locate and sync those subforms that haven't changed.
For wIndex = LBound(aControls()) To UBound(aControls())
    ' Extract name of subform control using Split:
    '   [subControlAny]
    Set wSubform = Me(Split(aControls(wIndex), "!")(ControlName.SubForm)).Form
    If wCount <> wSubform.RecordsetClone.RecordCount Then
       ' A record has been added or deleted in another subform.
       wSubform.Requery
    End If
    If IsNull(sControls(wIndex)) Or sControls(wIndex) <> wThisID Then
      ' This subform is to be synced.
      Set rst = wSubform.RecordsetClone
      ' Find record for current key.
      ' Extract name of control on subform using Split:
      '   [ID]
      ' Then use ControlSource to get the name of the field to search.
      wFieldName = wSubform(Split(aControls(wIndex), "!")(ControlName.Key)).ControlSource
      ' Wrap the fieldname in brackets in case it should contain spaces or special characters.
      If Left(wFieldName, 1) <> "[" Then
        wFieldName = "[" & wFieldName & "]"
     End If
     rst.FindFirst wFieldName & " = " & wThisID
     If Not rst.NoMatch Then
        bmk = rst.Bookmark
        wSubform.Bookmark = bmk
     End If
     rst.Close
    End If
Next

End If

Set rst = Nothing
Set wSubform = Nothing

SyncSubforms = wLastID

End Function

完整的文档和演示可供下载: Synchronizing Multiple Subforms in Access