Корень проблемы не в выборе, так как он существует и работает как положено:
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
элементы управления не являются реальными окнами, но«безоконный» объект без дескриптора окна (конечно, есть исключения, такие как listbox, tabstrip, multipage), который легко проверить с помощью скрытого метода:
'Which obviously returns a zero.
Debug.Print TextBox1.[_GethWnd]
С другой стороны, существует модель передачи сообщений Window, в которойкаждый элемент управления - это окно (отсюда и ОС Windows) с соответствующим дескриптором окна и способностью отправлять и получать сообщения, такие как 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>
--
В заключение, причина в том, что внутренние и внешние состояния фокуса вышли из синхронизации, что вытекает из немного другой модели управления между MSForms
и WinForms
/ WinAPI
плюс не-модальный режим работы, который смешивает их обоих, давая возможность потерять фокус на чем-то не MSForms
.