我有一个包含数据表的表格。我想让用户可以选择多行,单击一个按钮并运行一些 sql 查询并对这些行执行一些工作。
查看我的 VBA 代码,我了解如何使用 CurrentRecord 属性访问最后选择的记录。但是我不知道如何知道在多项选择中选择了哪些行。(我希望我很清楚......)
这样做的标准方法是什么?访问 VBA 文档在网上有些晦涩难懂...
谢谢!
这是执行此操作的代码,但有一个问题。
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
Catch 2:这只适用于连续的选择。他们不能在网格中选择多个非连续行。
更新:
可能有一个更好的事件来捕获这个,但这是一个使用我测试过的 form.timerinterval 属性的工作实现(至少在 Access 2k3 中,但 2k7 应该可以正常工作)
此代码进入 SUBFORM,使用该属性获取主窗体中的 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 事件。
所以在主窗体中:
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 中,我发现有时直接使用表和查询比尝试使用 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 事件并定义特定的键码和 shift 组合来执行您的代码。
JohnFx 提供的代码运行良好。我以这种方式在没有计时器的情况下实现了它(MS-Access 2003):
1- 将表单的键预览设置为是
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