对于每个在VBA代码中无法正常工作

For each not working properly in VBA code

提问人:Andrew G. Johnson 提问时间:12/25/2008 最后编辑:CommunityAndrew G. Johnson 更新时间:5/27/2015 访问量:1542

问:

好吧,我几乎完成了我在这里讨论的应用程序的审计部分。我这样做的方式是遍历所有文本字段、下拉框和复选框,并将它们的值存储在 form_load 事件中。然后我在form_afterUpdate事件中做同样的事情,并将两者进行比较。如果有差异,我会记录它,如果没有,我会继续前进。代码如下:

Dim strValues(1 To 32) As String

Private Sub Form_AfterUpdate()
    Dim strCurrentValue, strSQL As String
    Dim intCurrentField As Integer
    intCurrentField = 1

    For Each C In Forms!frmVendorsManageVendors.Controls
        Select Case C.ControlType
            Case acTextBox, acComboBox, acCheckBox
                //Doing this because I don't want a NULL as it won't concatenate in the SQL query and don't want 0 or -1 for the boolean fields
                strCurrentValue = IIf(IsNull(C), "", IIf(C = vbTrue Or C = vbFalse, IIf(C = vbTrue, "Yes", "No"), C))

                If strValues(intCurrentField) <> strCurrentValue Then
                    strSQL = "INSERT INTO changesTable (change_time,user_affected,field_affected,old_value,new_value) VALUES (NOW()," & [id] & ",'" & C.ControlSource & "','" & strValues(intCurrentField) & "','" & strCurrentValue & "')"

                    DoCmd.SetWarnings False
                    DoCmd.RunSQL strSQL
                    //InputBox "", "", strSQL
                    strSQL = "WEEEE"
                    DoCmd.SetWarnings True

                    strValues(intCurrentField) = strCurrentValue
                End If

                intCurrentField = intCurrentField + 1
        End Select
    Next
End Sub

Private Sub Form_Open(Cancel As Integer)
    Call btnLock_Click

    Dim intCurrentField As Integer
    intCurrentField = 1

    For Each C In Forms!frmVendorsManageVendors.Controls
        Select Case C.ControlType
            Case acTextBox, acComboBox, acCheckBox
                //Doing this because I don't want a NULL as it won't concatenate in the SQL query and don't want 0 or -1 for the boolean fields
                strValues(intCurrentField) = IIf(IsNull(C), "", IIf(C = vbTrue Or C = vbFalse, IIf(C = vbTrue, "Yes", "No"), C))
                intCurrentField = intCurrentField + 1
        End Select
    Next
End Sub

正如你所看到的,有一个注释掉的行,我插入到changesTable中,它将查询放在一个输入框中,这样我就可以复制/粘贴它并查看它。当我取消注释该行时,一切都很好。如果它被注释,则会生成第一个更改,但随后不会为其他控件更改它。因此,如果我更改字段 1 和字段 2,它将插入字段 1 更改两次。

这非常令人困惑,我不知道为什么会发生这种情况。

我也知道我使用了错误的注释语法,但是如果我使用正确的语法,SO“代码颜色”er将无法正确显示。

MS-Access VBA

评论

0赞 Russ Cam 12/25/2008
您可以通过在注释末尾放置另一个撇号/单引号,在代码 Markdown 中使用正确的注释语法。这会欺骗着色器认为注释是一个字符串,但它也允许任何人复制/粘贴您的代码并让它开箱即用
0赞 David-W-Fenton 12/27/2008
两件事: 1. // 不是 VBA 的注释分隔符,而是撇号。当我在 Access 代码中使用 // 时,它会抛出编译错误。2. 你似乎没有将 C 声明为变量,这表明你没有在所有代码模块中都有 OPTION EXPLICIT。这是可怕的编码实践。
0赞 Andrew G. Johnson 12/27/2008
大卫,你应该在评论之前阅读整个问题

答:

0赞 shahkalpesh 12/25/2008 #1

我猜 AfterUpdate 可能不是正确的事件。

此外,放置输入框可能会导致现有控件失去焦点(从而使其行为正确)。

我建议通过在选择大小写之后将 msgbox C.name 放入循环中来检查您的每个控件是否正在运行。

评论

0赞 Andrew G. Johnson 12/25/2008
我已经做了很多 msgbox 测试,所有字段都在运行(请记住,我提到过如果我添加侵入性输入框,这将完美工作)——但是我认为您可能正在失去焦点......
1赞 John Mo 12/27/2008 #2

我不确定我有完整的答案,但有几点观察。

可以使用 CurrentDB.Execute strSQL 消除某些代码行。这样就不需要 SetWarnings 调用。它直接针对数据库执行,而不与通常的接口机制交互。

出于调试目的,最好使用 Debug.Print 将 SQL 字符串放到“调试”窗口中。它避免了涉及用户界面,但仍然将 SQL 放在您可以将其复制到剪贴板的位置,如果您想获取它并使用它。

我认为执行 SQL 的 DoCmd 方法调用(即使调用 SetWarnnigs)的可能性很小,也可能在界面中倾斜一些东西以将焦点从表单上移开,就像 shahkalpesh 建议的那样。我已经做过这样的事情,但没有看到你遇到的问题,所以我对问题本身的唯一建议是像我一样做,切换到 CurrentDB.Execute 并消除循环中对 DoCmd 的调用。

只是好奇 -- 为什么你对以前的值使用数组,而不是在控件上使用 OldValue 属性?

0赞 BIBD 1/31/2009 #3

您是否尝试过使用执行语句(类似这样)来执行此操作?

Dim db As DAO.Database    'Inside the transaction.
Set db = CurrentDB
strSQL = "INSERT INTO changesTable (change_time, user_affected, " & _ 
            "field_affected, old_value, new_value) VALUES (NOW()," & [id] & _
            ",'" & C.ControlSource & "','" & strValues(intCurrentField) & _
            "','" & strCurrentValue & "')"
db.Execute strSql