无模式表单上的文本框输入的验证消息会中断文本选择

时间:2018-11-20 12:24:40

标签: excel vba excel-vba userform

嗨,我尝试在我的用户表单中检查以下代码,以检查在textbox1中输入的数据是否为数字,如果没有向用户显示msgbox并在textbox1中选择文本,但请在下面用户表单为textbox1

时,代码不会选择vBModeless中的文本
Private Sub TextBox1_Change()
    If Not IsNumeric(TextBox1) Then
        MsgBox " only number"
        TextBox1.SetFocus
        TextBox1.SelStart = 0
        TextBox1.SelLength = Len(TextBox1.Text)
    End If
End Sub

有什么解决办法吗?

3 个答案:

答案 0 :(得分:7)

问题的根源不是选择,因为它在那里并且按预期工作:

Private Sub TextBox1_Change()
    If Not IsNumeric(TextBox1) Then
        MsgBox " only number"
        TextBox1.SetFocus
        TextBox1.SelStart = 0
        TextBox1.SelLength = Len(TextBox1.Text)
        Debug.Print TextBox1.SelText
    End If
End Sub

我认为这里的根本问题是MSForms控件不是真正的窗口,而是没有窗口句柄的“无窗口”实体(当然,列表框,标签页,多页等例外情况)可以轻松进行测试通过隐藏方法:

'Which obviously returns a zero.
Debug.Print TextBox1.[_GethWnd]

另一方面,存在Window的消息传递模型,其中每个控件都是一个窗口(因此为Windows OS),具有适当的窗口句柄,并且能够发送和接收消息,例如WM_SETFOCUS / WM_KILLFOCUS和采取适当的行动。 回到MSForms-UserForm内部管理外部世界和子控件之间的所有交互。

让我们首先声明WIN API函数GetFocus

Public Declare Function GetFocus Lib "user32.dll" () As Long

让我们添加一些Debug.Print来看看发生了什么:

Private Sub TextBox1_Change()
    If Not IsNumeric(TextBox1) Then
        Debug.Print "--"
        Debug.Print GetFocus
        MsgBox " only number"
        Debug.Print GetFocus
        TextBox1.SetFocus
        Debug.Print GetFocus
        Debug.Print "--"
        TextBox1.SelStart = 0
        TextBox1.SelLength = Len(TextBox1.Text)
    End If
End Sub

产生以下顺序:

--
 <userform hwnd>
 <outer hwnd>
 <outer hwnd>
--

如您所见-SetFocus无效,因为用户窗体不知道失去焦点(因此也没有Exit事件)。要解决此问题,您应该通过将焦点转移到另一个子控件或切换Enabled(甚至是Visible)属性来明确失去焦点:

Private Sub TextBox1_Change()
    If Not IsNumeric(TextBox1) Then
        Debug.Print "--"
        Debug.Print GetFocus
        TextBox1.Enabled = False
        'or use CommandButton1.SetFocus or something
        MsgBox " only number"
        TextBox1.Enabled = True
        Debug.Print GetFocus
        TextBox1.SetFocus
        Debug.Print GetFocus
        Debug.Print "--"
        TextBox1.SelStart = 0
        TextBox1.SelLength = Len(TextBox1.Text)
    End If
End Sub

会产生所需的外观和适当的顺序:

--
 <userform hwnd>
 <outer hwnd>
 <userform hwnd>
--

结论是,原因是内部和外部焦点状态不同步,这是由于MSFormsWinForms / WinAPI之间的管理模型略有不同,以及模态的工作方式,将两者结合在一起,使人们有机会失去对非MSForms事物的关注。

答案 1 :(得分:6)

在我的Excel版本中,msgbox始终为vbModal,不能为vbModeless,您只能将其Modal范围属性设置为应用程序级别或系统级别

  • 在应用程序级别,它将停止应用程序,直到响应为止
  • 在系统级别,它将挂起所有应用程序,直到用户响应为止

为了做你打算做的事;我创建了一个无模式用户窗体并将其用作消息框

代码变为

Private Sub TextBox1_Change()

    If Not IsNumeric(TextBox1) Then
        UserForm2.Label1 = "Only Number is Allowed"
        UserForm2.Show

        'At this point TextBox1 has lost focus,
        'to set the focus again you have to setfocus on something else
        'and then again set focus on textbox1 (a way to reinitialize it).
        'I have added a hidden textbox2 and will set focus on it

        TextBox2.Visible = True
        TextBox2.SetFocus
        TextBox2.Visible = False

        TextBox1.SetFocus
        TextBox1.SelStart = 0
        TextBox1.SelLength = Len(TextBox1.Text)

    End If

End Sub

enter image description here

该屏幕截图仅是测试,您可以根据自己的应用进行格式化等。

答案 2 :(得分:1)

我投票支持usmanhaq和常识!

仅需添加一些内容:我试图在我的一个项目中实现类似的功能,但最终避免弹出另一个窗口。只需使用标签来提醒。

在执行此操作后,我发现它更加人性化!

希望这会有所帮助!

用户表单:

Private Sub TextBox1_Change()
If Not IsNumeric(TextBox1.Value) Then
    Label1.Caption = "NUMBER ONLY!"
    UserForm1.TextBox1.SetFocus
    UserForm1.TextBox1.SelStart = FirstNonDigit(TextBox1.Value) - 1
    UserForm1.TextBox1.SelLength = Len(TextBox1.Text)
Else
     Label1.Caption = ""
End If
End Sub

enter image description here

此功能是在线资助的,有助于从第一个非数字开始突出显示

Public Function FirstNonDigit(xStr As String) As Long
    Dim xChar As Integer
    Dim xPos As Integer
    Dim I As Integer
    Application.Volatile
    For I = 1 To Len(xStr)
        xChar = Asc(Mid(xStr, I, 1))
        If xChar <= 47 Or _
           xChar >= 58 Then
            xPos = I
            Exit For
        End If
    Next
    FirstNonDigit = xPos
End Function