При посыле команды Alt+Down меняется индикатор NumLock. (решено, пост №9) По-моему это глюк Excel.
Прикрепил файл, но по сути он чист. Только в "Лист1" добавлен макрос ну и создан список для теста:
Код
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim lValidation As Long
On Error Resume Next
lValidation = ActiveCell.Validation.Type
On Error GoTo 0
If lValidation = 3 Then Application.SendKeys "%{DOWN}"
End Sub
Подробно. Если выделить ячейку с проверкой данных, то посылается команда Alt+Down, что бы список сам раскрылся. Для удобства. Однако вместе с этим, меняется индикатор NumLok, причем, если в какой-то момент нажать NumLock самому (пальцем на клавиатуре), то он как ему и положено изменится и больше меняться от выделения ячейки не будет. Если нажать еще раз, то снова начнет меняться от выделения ячейки.
На другом форуме уже задавал этот вопрос, сказали смотри машину. Проверял на 4-х разных машинах (на одной 3 недели назад винду переустанавливали, так что чище некуда) с разными клавиатурами. Эффект один.
При установке винды все равно устанавливают драйвера и специальные утилиты. Могу предположить, что на последнем компе поставили графическую утилиту от Nvidia или еще чего нибудь. Попробуйте у них поискать Hotkey-и и вообще отключить их
Не указал сразу. Если на клавиатуре выполнить Alt + "стрелка вниз", то что при левом, что при правом Alt, NumLoсk не меняется. В т.ч. если выделена ячейка со списком. Список раскрывается и сворачивается, а NumLock не реагирует. Реагирует только на команду из скрипта.
У меня действительно nVidia, не знаю где там что может быть с хоткеями, но поищу, хоть и сомневаюсь что найду что-то. И еще, отпишитесь кто-нибудь у кого именно этот макрос нормально выполняется, без смены NumLock (желательно проверить в приложенном файле). А то ощущение, что советуют не проверив. Просто для уверенности, мол "Я, такой-то проверял, у меня все как надо, ищи ошибку у себя". Редко бывает, что на разных машинах один и тот же глюк.
Ок, попробуем обойти. Где-то месяц назад я писал макрос с использованием этого трюка. На VBA до этого не писал так что уже подзабыл что да как. Смысл в том, что как и рекомендовано в Mictosoft по ссылке выше, я пытался запомнить значение NumLock и после восстановить его. Считывал значение 2-3 разными методами, но возвращалось всегда одно и то же значение. Так и не разобрался тогда. Может кто напишет пару строк работающего кода?
Но в 1-й строчке как ни крутил, всегда true возвращалось (или false, не помню уже) . (на всякий случай: 1 и 3 строчки используют несуществующие методы Get/Set, это только пример как должно работать)
Предложения от Microsoft: Чтобы обойти эту проблему, выполните одно из следующих: Определите параметр клавиши NumLock до с помощью SendKeys. Затем, Отключите NumLock перед использованием SendKeys. После использования SendKeys, сброс NumLock его предыдущего значения. Это достигается с помощью GetKeyboardState, keybd_event и SetKeyboardState API функций. См. в разделе ссылки ниже для получения дополнительной информации. ИЛИ С помощью функций API вместо SendKeys
вот так вроде бы возвращает статус в зависимости от лампочки.
Код
Private Declare PtrSafe Function GetKeyState Lib "user32.dll" (ByVal nVirtKey As Long) As Integer
Sub Get_NumLock()
Dim before as Integer
before = GetKeyState(vbKeyNumlock)
End Sub
А вот как установить статус кнопки обратно - это я не знаю пытался дописать в ваш код SendKeys({NUMLOCK}) - он моргает и сбрасывается. Наверняка есть какая-то API-функция, которая "нажимает" NumLock.
И все-таки это возможно. Коротко и с готовым примером (файл прилагается).
Перед
Код
Application.SendKeys "%{DOWN}"
надо выполнить
Код
If Num_Not_Stable() Then Application.SendKeys "{NUMLOCK}"
и NumLock меняться не будет.
В листе:
Код
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim lValidation As Long
If Num_Not_Stable() Then Application.SendKeys "{NUMLOCK}" ' делает NumLock стабильным
On Error Resume Next
lValidation = ActiveCell.Validation.Type
On Error GoTo 0
If lValidation = 3 Then Application.SendKeys "%{DOWN}"
End Sub
В модуле книги:
Код
Declare PtrSafe Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long
' Определяет, изменчивое ли состояние у NumLock или нет
' Возвращает false - стабильный, true - изменчевый
Function Num_Not_Stable() As Boolean
Dim keystat(0 To 255) As Byte
Dim state As String
GetKeyboardState keystat(0)
state = keystat(vbKeyNumlock)
If (state = 0) Then
Num_Not_Stable = False
Else
Num_Not_Stable = True
End If
End Function
Теперь рассуждения что, и как работает.
Посмотрев по предложенным ссылкам и многим другим источникам, убедился- все это шляпа. И она не работает. Я не то что не гуру, использую VBA около месяца. Потому, все что ниже (и выше, не счет "шляпы" может не соответствовать истине. Кроме того, что бы придти к этому способу я сознательно подменил стандартные понятия своими, заведомо не верными, но работающими в данном контексте.
И так. Первое понятие: команда {NUMLOCK} не посылает сигнал "нажать NumLock", а закрепляет NumLock, если он "изменчивый".
"Изменчивый" - это когда он меняет свое состояние, когда не должен, например при команде %{DOWN} которая по идее должна только сымитировать Alt+Down.
Экспериментальным путем выяснил, что NumLock имеет "стабильное" состояние и "изменчивое". Не путать с "включен" и "выключен", в любом из них он может быть как "изменчивым" так и "стабильным".
Второе понятие: строчки GetKeyboardState keystat(0) state = keystat(vbKeyNumlock) определяют не текущее состояние NumLock, а его текущую стабильность. Если state = 0, то NumLock стабилен, иначе изменчив.
Теперь все просто. Если NumLock стабилен, ничего не делаем, просто посылаем команду %{DOWN}. Если NumLock изменчивый, посылаем команду стабилизации (закрепления) {NUMLOCK}, и после уже спокойно пишем %{DOWN}, зная, что NumLock не изменится.
Конечно понятия "стабильный" и "изменчивый" надуманы но тут они отлично подходят.
Несколько фактов из наблюдений. В стабильном состоянии следущие команды не меняют NumLock %{DOWN} {NUMLOCK} {NUMLOCK} (т.е. два раз подряд послать нажатие NumLock)
В изменчивом состоянии те же команды его меняют регулярно (при каждой команде NumLock 1 раз меняется) оставляя его изменчивым. При этом команда {NUMLOCK} (т.е. один раз послать нажатие NumLock) работает так (допустим сейчас стабильное состояние, и он включен (лампочка горит)): 1. {NUMLOCK} - NumLock изменился (погас), состояние стало изменчивым 2. {NUMLOCK} - NumLock не изменился (не горит), состояние стало стабильным 3. {NUMLOCK} - NumLock изменился (загорелся), состояние стало изменчивым 4. {NUMLOCK} - NumLock не изменился (горит), состояние стало стабильным ...... и далее по кругу
Еще немного мелочей. Во многих гайдах пишут подключить библиотеку для работы GetKeyboardState keystat(0), а именно: Declare PtrSafe Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long PtrSafe - там отсутствует, что вызывает ошибку, т.к. сейчас почти у всех Windows 64 bit, а не 32. Для совместимости и надо добавить.
Так же в этом объявлении для совместимости с 64-битной версией, рекомендуют использовать тип LongLong вместо Long. Возможно это где-то оправдано. У меня работали оба варианта, но у одного из тех, кому я делал вылезла ошибка, что у него нет такого типа данных (LongLong). По этому пишу просо Long. Что у меня, что у него Windows 7 64bit, Excel 2010.
P.S. мда, ну и накатал. Что значит заняться не чем было (5 часов разбирался). Ну да может пригодится кому.
Dmitri_0154, Ваша настойчивость в достижении результата достойна уважения, которое я Вам и выражаю снимая шляпу. (хотя в жизни шляп не носил и не собираюсь )
Формула массива (ФМ) вводится Ctrl+Shift+Enter Memento mori
Спасибо за пояснения. Вот еще из MSDN, из пояснений к функции GetKeyState (так и не понял, зачем использовать GetKeyboardState, если мы хотим узнать только одну кнопку), какие она возвращает состояния (она возвращает Integer):
Цитата
0 if the key is neither down nor toggled, -127 if the key is down but not toggled, 1 if the key is toggled but up, and -128 if the key is both toggled and down.
где toggled - это якобы когда лампочка горит. GetKeyboardState возвращает 129 вместо -127, 128 вместо -128 Соответственно, можно плясать от этих значений сразу, не пользуя UDF?
А вот то, что SendKeys "{NumLock}" не переключает сразу состояние, а бегает по этому кругу - вот это новость )) сейчас буду ее логгить
теперь понятно почему на этот код программа реагирует странно - то включает, то не включает. Всё дело в состоянии. А как тогда можно сделать нормально работающий код, который бы гарантировано включал NumLock при загрузке формы?
Код
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Public Function KeyboardNumLock()
Dim WsNumlock As Variant
If GetKeyState(vbKeyNumlock) = 0 Then
Set WsNumlock = CreateObject("WScript.Shell")
WsNumlock.SendKeys "{NUMLOCK}"
End If
End Function
Option Explicit
Private Declare Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Const VK_NUMLOCK = &H90
Const VK_SCROLL = &H91
Const VK_CAPITAL = &H14
Const KEYEVENTF_EXTENDEDKEY = &H1
Const KEYEVENTF_KEYUP = &H2
Sub Управление_кнопками()
ManageButtons (False) 'выключить кнопки
ManageButtons (True) 'включить кнопки
End Sub
Private Sub ManageButtons(ByVal State As Boolean)
'https://support.microsoft.com/ru-ru/kb/177674/en-us
Dim NumLockState As Boolean
Dim ScrollLockState As Boolean
Dim CapsLockState As Boolean
Dim keys(0 To 255) As Byte
GetKeyboardState keys(0)
' NumLock
NumLockState = keys(VK_NUMLOCK)
If NumLockState <> State Then
keybd_event VK_NUMLOCK, &H45, KEYEVENTF_EXTENDEDKEY Or 0, 0 'Simulate Key Press
keybd_event VK_NUMLOCK, &H45, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0 'Simulate Key Release
End If
' CapsLock
CapsLockState = keys(VK_CAPITAL)
If CapsLockState <> State Then
keybd_event VK_CAPITAL, &H45, KEYEVENTF_EXTENDEDKEY Or 0, 0 'Simulate Key Press
keybd_event VK_CAPITAL, &H45, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0 'Simulate Key Release
End If
' ScrollLock
ScrollLockState = keys(VK_SCROLL)
If ScrollLockState <> State Then
keybd_event VK_SCROLL, &H45, KEYEVENTF_EXTENDEDKEY Or 0, 0 'Simulate Key Press
keybd_event VK_SCROLL, &H45, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0 'Simulate Key Release
End If
End Sub
В Access столкнулся с проблемой переключения NumLock при использовании SendKeys. Помог WS. При нажатии одной кнопки надо, чтобы "нажалась" другая. Если использовать SendKeys - NumLock переключается, а так:
Код
Private Sub NB2_Click()
Me.NB1.SetFocus: SendSomeKey "~", -1
End Sub
Sub SendSomeKey(Code As String, Optional Wait As Boolean)
Dim ws As Variant
Set ws = CreateObject("WScript.Shell")
ws.SendKeys Code, Wait
Delay 0.5 ' подождать 1/2 секунды любым доступны в VBA способом
End Sub
Спасибо за сообщение. Да, я тоже замечал, что при использовании метода SendKeys объекта Wscript.Shell проблемы с переключателями NumLock и CapsLock не возникают (в отличие от оператора VBA SendKeys и метода Application.SendKeys).
Вот и я столкнулся с этой проблемой и воспользовался исследованиями предыдущих ораторов. Мои пять копеек: Не зря предостерегают от SendKeys: мало того, что во время работы клавиатуру не тронь, так еще и мусорит, и "соседние кнопки" умудряется задеть.
Результат проверки GetKeyboardState и GetKeyState - это, насколько я понял: считается ли клавиша НАЖАТОЙ или ОТПУЩЕННОЙ!!! Именно эти состояния автор темы именовал "стабильным" и "изменчивым" (только в каком сочетании - не разобрался я) А SENDKEYS, как я понял, передает за один раз не целое переключение: кнопка нажата и отпущена, а только его половину. Такие переключатели, как NUMLOCK совершают цикл за два целых переключения, стало быть, за 4 SENDKEYS. Вот и объяснение 4-х состояний для NUMLOCK.
Мне, кстати, не удалось добиться от GetKeyboardState и GetKeyState ничего, кроме 0 или 1, свидетельствующих (как уважаемые форумчане точно подметили) нажата клавиша или отпущена.
Следствие из третьего закона Чизхолма: "Даже если ясность изложения исключает неверное толкование, все равно найдется кто-то, кто поймет Вас неправильно."