替代 SendKeys "{Esc}" 用于未绑定的文本框撤消

Alternative to SendKeys "{Esc}" for unbound text box Undo

我有一个 未绑定 文本框控件。用户输入一些有效文本并离开控件。然后用户 returns 到控件并输入一些无效文本。我想向用户显示一条消息,然后 将控件的值回滚到其先前的状态 将焦点保持在该控件中 .

我尝试了以下方法,none 正是我要找的方法:


选项 A:发送密钥

Private Sub MyTextBox_BeforeUpdate(Cancel As Integer)
    Cancel = DataIsInvalid(Me.MyTextBox.Value)
    If Cancel Then SendKeys "{Esc}"
End Sub    

这正是我想要的,但我真的想避免使用 SendKeys。使用 SendKeys 会带来很多问题:Vista+ compatibility、将密钥发送到不同的应用程序等。


选项 B:撤消控制方法

Private Sub MyTextBox_BeforeUpdate(Cancel As Integer)
    Cancel = DataIsInvalid(Me.MyTextBox.Value)
    If Cancel Then Me.MyTextBox.Undo
End Sub    

这对于未绑定的控件来说是简单的破坏(至少从 MS Access 2002/XP 开始)。此方法不会将 MyTextBox 的值恢复为有效输入。但是,它确实允许用户将焦点更改为新控件,同时将无效输入保留在 Me.MyTextBox 中!难以置信!!!


选项 C: 撤销表单的方法

Private Sub MyTextBox_BeforeUpdate(Cancel As Integer)
    Cancel = DataIsInvalid(Me.MyTextBox.Value)
    If Cancel Then Me.Form.Undo
End Sub    

这里的Undo什么都不做。但至少它不会破坏 BeforeUpdate Cancel=True 代码并允许无效数据存在。


选项 D:显式恢复更新前事件中的旧值

Private mPrevMyTextBoxValue As Variant

Private Sub MyTextBox_AfterUpdate()
    mPrevMyTextBoxValue = Me.MyTextBox.Value
End Sub
Private Sub MyTextBox_BeforeUpdate(Cancel As Integer)
    Cancel = DataIsInvalid(Me.MyTextBox.Value)
    If Cancel Then Me.MyTextBox.Value = mPrevMyTextBoxValue
    'If Cancel Then Me.MyTextBox.Text = mPrevMyTextBoxValue
End Sub    

尝试将先前的值分配给文本框的 .Value.Text 属性 会导致相同的错误消息:

The macro or function set to the BeforeUpdate or ValidationRule property for this field is preventing {Program Name} from saving the data in the field.


选项 E: 在 AfterUpdate 事件中显式恢复旧值

Private mPrevMyTextBoxValue As Variant

Private Sub MyTextBox_AfterUpdate()
    If DataIsInvalid(Me.MyTextBox.Value) Then
        Me.MyTextBox.Value = mPrevMyTextBoxValue
        Me.MyTextBox.SetFocus
    Else
        mPrevMyTextBoxValue = Me.MyTextBox.Value
    End If
End Sub

这非常接近所需的行为。不幸的是,无法将焦点保持在控件上,因为 AfterUpdate 事件在 处理 Tab/Enter 按键或鼠标单击事件之前运行。因此,即使我们试图将焦点强制到适当的控件(通过上面的 .SetFocus 语句),程序也会立即将焦点转移到用户选择的控件。


在我看来,"right"的方法是使用控件的.Undo方法。但是,这不适用于未绑定的控件。这是一个莫名其妙的缺点,特别是考虑到 Escape 按键对未绑定字段执行此功能这一事实。

有没有更好的方法来做到这一点,还是我应该坚持使用 SendKeys

如果是unbound,为什么不使用AfterUpdate事件:

Private Sub MyTextBox_AfterUpdate()
    If DataIsInvalid(Me!MyTextBox.Value) Then
        Me!MyTextBox.Value = Null
        Me!MyTextBox.SetFocus
    End If
End Sub   

或修改您的其他解决方案:

Private Sub MyTextBox_AfterUpdate()

    Static mPrevMyTextBoxValue As Variant

    If IsEmpty(mPrevMyTextBoxValue) Then
        mPrevMyTextBoxValue = Null
    End If

    If DataIsInvalid(Me!MyTextBox.Value) Then
        Me!MyTextBox.Value = mPrevMyTextBoxValue 
        Me!MyTextBox.SetFocus
    Else
        mPrevMyTextBoxValue = Me!MyTextBox.Value
    End If
End Sub

尝试将焦点从文本框移开,然后再返回:

        Me!MyTextBox.Value = mPrevMyTextBoxValue 
        Me!SomeOtherControl.SetFocus
        Me!MyTextBox.SetFocus

其他控件可以是一个几乎隐藏的小文本框。

进入 google 的第 3 页后,我决定四处看看,看看在尝试选项 E 时会发生什么样的排序。以下是我能够用来集中注意力的最佳解决方法"Stay" 在有问题的文本框上。

Private mPrevMyTextBoxValue As Variant

Private Sub Text0_AfterUpdate()
    If Me.Text0.Value = 0 Then
        Me.Text0.Value = mPrevMyTextBoxValue
        Me.Text12.SetFocus
        Me.Text0.SetFocus
    Else
        mPrevMyTextBoxValue = Me.Text0.Value
    End If
End Sub

0 模拟验证失败。似乎如果您在将焦点设置回您正在使用的文本框之前将焦点设置到其他内容,它将停留在那里。不幸的是,我没有答案。

编辑:我已决定尝试对我的理论进行编程以了解其工作原理。

Private blnGoToNextControl as Boolean

Private Function SetFocus(ctrl As control)

   blnGoToNextControl = True
   If ctrl.HasFocus = True Then 
    'Do nothing
   Else 
      ctrl.HasFocus = True 
      blnGoToNextControl = False
   End If
End Function 

这是我对 SetFocus 函数工作原理的看法。因此,在 AfterUpdate 事件运行后,它将检查是否转到下一个控件的标志,并查看它是否设置为 false 并且不转到下一个控件。

显然编码粗糙,但希望这能让我的理论得到理解?