Как получить доступ к выбранным строкам в Access?

У меня есть форма, которая включает в себя паспорт. Я хотел бы, чтобы пользователь мог выбрать несколько строк, нажать кнопку и запустить sql-запрос и выполнить некоторую работу над этими строками.

просматривая мой код VBA, я вижу, как я могу получить доступ к последней выбранной записи, используя свойство CurrentRecord. Тем не менее, я не вижу, как я могу знать, какие строки были выбраны в множественном выборе. (Надеюсь, я ясно выразился...)

что стандартный способ сделать это? Доступ к документации VBA несколько неясен в сети...

спасибо!

9 ответов


вот код, чтобы сделать это, но есть одна загвоздка.

Private Sub Command1_Click()
     Dim i As Long
     Dim RS As Recordset
     Dim F As Form

     Set F = Me.sf.Form
     Set RS = F.RecordsetClone

     If F.SelHeight = 0 Then Exit Sub

     ' Move to the first selected record.
     RS.Move F.SelTop - 1

     For i = 1 To F.SelHeight
       MsgBox RS![myfield]
       RS.MoveNext
     Next i

End Sub

вот подвох: Если код добавляется к кнопке, как только пользователь нажимает эту кнопку, выбор теряется в сетке (selheight будет равен нулю). Поэтому вам нужно захватить эту информацию и сохранить ее в переменной уровня модуля с таймером или другими событиями в форме.

вот статья, описывающая, как обойти улов в некоторых деталь.
http://www.mvps.org/access/forms/frm0033.htm

Лови 2: это работает только с непрерывной выборки. Они не могут выбрать ряд непоследовательных строк в сетке.

обновление:
Возможно, есть лучшее событие, чтобы поймать это, но вот рабочая реализация с использованием формы.свойство timerinterval, которое я тестировал (по крайней мере, в Access 2k3, но 2k7 должен работать нормально)

этот код идет в ПОДФОРМЕ используйте свойство, чтобы получить значение selheight в главной форме.

Public m_save_selheight As Integer

Public Property Get save_selheight() As Integer
    save_selheight = m_save_selheight
End Property

Private Sub Form_Open(Cancel As Integer)
    Me.TimerInterval = 500
End Sub

Private Sub Form_Timer()
    m_save_selheight = Me.selheight
End Sub

я использовал технику, подобную JohnFx

чтобы поймать высоту выделения до ее исчезновения, я использовал событие Exit элемента управления subform в основной форме.

Итак, в основной форме:

Private Sub MySubForm_Exit(Cancel As Integer)

  With MySubForm.Form
    m_SelNumRecs = .SelHeight
    m_SelTopRec = .SelTop
    m_CurrentRec = .CurrentRecord
  End With

End Sub

Я пробовал делать что-то подобное раньше, но у меня никогда не было успеха с использованием метода, который требовал, чтобы пользователь выбирал несколько строк в том же стиле, что и диалоговое окно файла Windows (нажатие Ctrl, Shift и т. д.).

один из методов, который я использовал, - использовать два списка. Пользователь может дважды щелкнуть элемент в левом списке или нажать кнопку при выборе элемента,и он переместится в правый список.

другой вариант-использовать локальную таблицу заполняется исходными данными плюс логическими значениями, представленными в виде флажков в подформе. После того, как пользователь выбирает, какие данные они хотят, нажав на флажки, пользователь нажимает кнопку (или какое-либо другое событие), в это время вы переходите непосредственно к базовой таблице данных и запрашиваете только те строки, которые были проверены. Я думаю, что этот вариант является лучшим, хотя для правильной работы требуется немного кода.

даже в Access, я нахожу, что иногда легче работать с таблицами и запросы напрямую, а не пытаться использовать встроенные инструменты в формах доступа. Иногда встроенные инструменты не делают именно то, что вы хотите.


обходной путь к потере выбора, когда подформа теряет фокус, заключается в сохранении выбора в событии Exit (как уже упоминалось другими).

хорошим дополнением является его немедленное восстановление с помощью таймера, чтобы пользователь все еще мог видеть сделанный им выбор.

Примечание: Если вы хотите использовать выделение в обработчике кнопок, выделение может не быть восстановлено уже при его выполнении. Обязательно используйте сохраненные значения из переменных или добавьте DoEvents в начале обработчика кнопки, чтобы позволить обработчику таймера выполнить первый.

Dim m_iOperSelLeft As Integer
Dim m_iSelTop As Integer
Dim m_iSelWidth As Integer
Dim m_iSelHeight As Integer

Private Sub MySubForm_Exit(Cancel As Integer)

    m_iSelLeft = MySubForm.Form.SelLeft
    m_iSelTop = MySubForm.Form.SelTop
    m_iSelWidth = MySubForm.Form.SelWidth
    m_iSelHeight = MySubForm.Form.SelHeight

    TimerInterval = 1

End Sub

Private Sub Form_Timer()

    TimerInterval = 0

    MySubForm.Form.SelLeft = m_iSelLeft - 1
    MySubForm.Form.SelTop = m_iSelTop
    MySubForm.Form.SelWidth = m_iSelWidth
    MySubForm.Form.SelHeight = m_iSelHeight

End Sub

есть другое решение.

код ниже покажет количество выбранных строк, как только вы отпустите кнопку мыши. Сохранение этого значения сделает трюк.

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)

        MsgBox Me.SelHeight

End Sub

используйте глобальную переменную в форме, затем обратитесь к ней в коде кнопки.

Dim g_numSelectedRecords as long

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
   g_numSelectedRecords = Me.SelHeight
End Sub


Dim formRecords As DAO.Recordset
Dim i As Long

Set formRecords = Me.RecordsetClone

' Move to the first record in the recordset.
formRecords.MoveFirst

' Move to the first selected record.
formRecords.Move Me.SelTop - 1

For i = 1 To numSelectedRecords
    formRecords.Edit
    formRecords.Fields("Archived") = True
    formRecords.Update
    formRecords.MoveNext
Next i

Почему бы не использовать массив или набор записей, а затем каждый раз, когда пользователь нажимает на строку (смежную или нет, сохраните эту строку или некоторый идентификатор в наборе записей. Затем, когда они нажимают кнопку в родительской форме, просто повторите набор записей, который был сохранен, чтобы сделать то, что вы хотите. Только не забудьте очистить массив или набор записей после нажатия кнопки.?


еще один обходной путь для сохранения выбора при попытке выполнить процедуру-вместо того, чтобы оставлять таблицу данных для активации кнопки, просто используйте событие OnKeyDown и определите конкретную комбинацию клавиш и сдвига для выполнения кода.


код JohnFx работает хорошо. Я реализовал его без таймера таким образом (MS-Access 2003):
1-Установите предварительный просмотр ключа формы в Yes
2-Поместите код в функцию
3-Установите событие OnKeyUp и OnMouseUp для вызова функции.

Option Compare Database
Option Explicit

Dim rowSelected() As String

Private Sub Form_Load()
'initialize array
ReDim rowSelected(0, 2)
End Sub

Private Sub Form_Current()
' if cursor place on a different record after a selection was made 
' the selection is no longer valid
If "" <> rowSelected(0, 2) Then
  If Me.Recordset.AbsolutePosition <> rowSelected(0, 2) Then
    rowSelected(0, 0) = ""
    rowSelected(0, 1) = ""
    rowSelected(0, 2) = ""
  End If
End If
End Sub

Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer)
rowsSelected
If KeyCode = vbKeyDelete And Me.SelHeight > 0 Then
    removeRows
End If
End Sub

Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
rowsSelected
End Sub

Sub rowsSelected()
Dim i As Long, rs As DAO.Recordset, selH As Long, selT As Long
selH = Me.SelHeight
selT = Me.SelTop - 1
If selH = 0 Then
    ReDim rowSelected(0, 2)
    Exit Sub
Else
    ReDim rowSelected(selH, 2)
    rowSelected(0, 0) = selT
    rowSelected(0, 1) = selH
    rowSelected(0, 2) = Me.Recordset.AbsolutePosition ' for repositioning 
    Set rs = Me.RecordsetClone
    rs.MoveFirst ' other key touched caused the pointer to shift
    rs.Move selT
    For i = 1 To selH
        rowSelected(i, 0) = rs!PositionNumber
        rowSelected(i, 1) = Nz(rs!CurrentMbr)
        rowSelected(i, 2) = Nz(rs!FutureMbr)
        rs.MoveNext
    Next
    Set rs = Nothing
    Debug.Print selH & " rows selected starting at " & selT 
End If
End Sub

Sub removeRows()
' remove rows in underlying table using collected criteria in rowSelected()
    Me.Requery
' reposition cursor
End Sub

Private Sub cmdRemRows_Click()
If Val(rowSelected(0, 1)) > 0 Then
    removeRows
Else
    MsgBox "To remove row(s) select one or more sequential records using the record selector on the left side."
End If
End Sub