Visual studio нажатие клавиши

Обновлено: 25.11.2022

Когда происходит нажатие клавиши, часто бывает необходимо знать больше, чем просто какая именно клавиша была нажата. Кроме этого, важно знать, какие еще клавиши были прижаты в это же время. Это означает, что может понадобиться проанализировать состояние остальных клавиш, особенно модификаторов вроде , и .

События клавиш (PreviewKeyDown, KeyDown, PreviewKeyUp и KeyUp) позволяют получить эту информацию. Во-первых, объект KeyEventArgs содержит свойство KeyState, которое отражает свойство клавиши, сгенерировавшей событие. Есть еще одно, более полезное, свойство KeyboardDevice, которое предоставляет такую же информацию для любой клавиши на клавиатуре.

Неудивительно, что свойство KeyboardDevice предоставляет экземпляр класса KeyboardDevice. Его свойства содержат информацию о том, какой элемент в данный момент имеет фокус (FocusedElement) и какие клавиши-модификаторы были прижаты в момент возникновения события (Modifiers). К клавишам-модификаторам относятся , и ; их состояние можно проверить с помощью следующего кода:

Класс KeyboardDevice тоже предоставляет несколько удобных методов, которые перечислены ниже. Каждому из них нужно передать значение из перечисления Key:

IsKeyDown()

Сообщает, была ли прижата данная клавиша в момент возникновения события

IsKeyUp()

Сообщает, была ли отпущена (не прижата) данная клавиша в момент возникновения события

IsKeyToggled()

Сообщает, находилась ли данная клавиша во "включенном" состоянии в момент возникновения события. Это относится лишь к клавишам, которые могут быть включены или выключены: , и

GetKeyStates()

Возвращает одно или несколько значений из перечисления KeyStates и сообщает, является ли данная клавиша прижатой, отпущенной, включенной или выключенной. По сути, дублирует вызов методов IsKeyDown() и IsKeyUp() с передачей им той же клавиши

При использовании свойства KeyEventArgs.KeyboardDevice код получает состояние виртуальной клавиши — состояние клавиатуры в момент возникновения события. Оно не обязательно совпадает с текущим состоянием клавиатуры. Например, допустим, что пользователь вводит данные быстрее, чем выполняется их обработка в коде. При каждом возникновении события KeyPress вы будете иметь доступ к клавише, сгенерировавшей событие, а не к уже введенным символам. Как правило, именно такое поведение и нужно.

Однако в событиях клавиатуры вы не ограничены получением лишь информации о клавише. Получать состояние клавиатуры можно в любой момент времени. Для этой цели предназначен класс Keyboard, который очень похож на класс KeyboardDevice, но состоит из статических членов. Вот пример использования класса Keyboard для проверки текущего состояния левой клавиши :

Класс Keyboard содержит также методы, которые позволяют прикреплять обработчики событий клавиатуры уровня всего приложения: AddKeyDownHandler() и AddKeyUpHandler(). Однако применять эти методы не рекомендуется. Лучше реализовать функциональность уровня приложения с помощью системы команд WPF.

2 ответа 2

WinForms и клавиатура, довольно веселое сочетание, особенно когда пытаешься их заставить работать согласованно.

1. События Control.Key*

Любой контрол умеет реагировать на нажатия клавиш и для этого определены несколько событий: Control.KeyDown, Control.KeyPress, Control.KeyUp. Вроде бы все нормально, подписываемся на события и получаем реакцию на клавиши. Но тут есть один подвох - эти события срабатывают только когда контрол находится в фокусе. Кроме того, многие контролы уже имеют стандартные обработчики клавиш, многие реагируют на пробел как на клик, а TextBox и его родственники перехватывают почти всю клавиатуру.

2. Форма тоже контрол

Класс Form также наследуется от контрол, и умеет получать эти события. Вроде бы очевидно, переносим обработку клавиш в события формы и радуемся, она же вроде бы всегда в фокусе. Но и тут не все так просто. Если дочерний контрол настроен на обработку клавиш и находится в фокусе (а если на форме есть дочерние контролы, то один из них обязательно в фокусе), то форма просто игнорирует нажатия на клавиши.

Можно вспомнить о свойстве формы Form.KeyPreview, которое предназначено для перехвата событий, тогда события клавиатуры будут сначала передаваться в форму. Это вроде бы рабочий вариант, но если вы не хотите чтобы стандартный функционал контролов все еще работал, то придется не забыть выставить свойство KeyPressEventArgs.Handled = false; чтобы после обработки события клавиатуры на уровне формы, оно было передано в дочерние контролы. Честно говоря, это не самый лучший вариант, т.к. имеет свои ограничения и далеко не все задачи можно решить таким способом. Хотя он вроде для этого и задуман.

3. Нас спасет меню

Первый вариант решения проблемы относительно без шаманства - добавить на форму меню в лице MenuStrip. Меню сделано таким образом, что к каждому его элементу, можно привязать хоткей, а для их корректной работы меню перехватывает все события клавиатуры и отдает дальше только те, которые не связаны с нажатиями на заданные клавиши и сочетания. Способ всем хорош, кроме пары моментов: для всех нужных клавиш и сочетаний нужен пункт в меню и необходимо само меню на форме. Если первая проблема еще может быть решена применением скрытых элементов (лично не проверял), то вторая - реально проблема, т.к. не любая форма приложения допускает наличие на ней меню, а добавлять его только ради обработки клавиш. ну вы меня поняли.

4. Универсальное решение

Не торопитесь радоваться, все предыдущие пункты требовали только мастерского владения кликом мыши в интерфейс конструктора, а тут потребуется писать код.

В форме, как и любом контроле есть виртуальный метод ProcessCmdKey, который отвечает за дефолтную обработку событий клавиатуры, вот его-то и потребуется переопределить в нашей форме для получения ожидаемой и корректной работы с клавиатурой.

Вызов базового метода - не обязателен. За передачу события дочерним контролам отвечает результат работы метода, только логика немного наоборот: true - клавиша обработана, дальнейшая обработка не требуется; false - событие будет передано дочерним контролам, даже если вы выполнили какую-то предварительную обработку. Таким образом вы получаете полный контроль над обработкой событий клавиатуры на уровне формы или кастомного контрола.

5. Ну и как этим воспользоваться?

Если хватит энтузиазма, позже соберу в этом ответе ссылки на связанные вопросы с хорошими ответами. А пока первое что нашлось по теме без глубокого копания:

I'm assuming its wrapped around a while loop. I don't like ReadKey as it blocks operation and asks for a key, rather than just continue and listen for the key press.

How can this be done?

10 Answers 10

Use Console.KeyAvailable so that you only call ReadKey when you know it won't block:

but if you do this instead of readLine() you lose the awesome feature of having history recall by pressing "up" key.

You can change your approach slightly - use Console.ReadKey() to stop your app, but do your work in a background thread:

In the myWorker.DoStuff() function you would then invoke another function on a background thread (using Action<>() or Func<>() is an easy way to do it), then immediately return.

The shortest way:

Console.ReadKey() is a blocking function, it stops the execution of the program and waits for a key press, but thanks to checking Console.KeyAvailable first, the while loop is not blocked, but running until the Esc is pressed.

We could do following to have multiple running process

Can you explain a little about the approach you had i am trying to do the same with my console application.An explanation for the code would be great.

@nayefharb setup a bunch of tasks, start them, and WaitAll will wait until they have all returned. Hence the individual tasks won't return and it will continue forever until a CancelKeyPress is triggered.

Console.CursorVisible = false; will be best to avoid the cursor blink glitches. Add this line as first line in the static void Main(string[] args) block.

Here is an approach for you to do something on a different thread and start listening to the key pressed in a different thread. And the Console will stop its processing when your actual process ends or the user terminates the process by pressing Esc key.

If you are using Visual Studio, then you can use "Start Without Debugging" in the Debug menu.

It will automatically write "Press any key to continue . . ." to the console for you upon completion of the application and it will leave the console open for you until a key is pressed.

Addressing cases that some of the other answers don't handle well:

  • Responsive: direct execution of keypress handling code; avoids the vagaries of polling or blocking delays
  • Optionality: global keypress is opt-in; otherwise the app should exit normally
  • Separation of concerns: less invasive listening code; operates independently of normal console app code.

The main issue to be aware of is that, by default, your console thread isn't set up for Async operation--meaning that, when you fall out of the bottom of your main function, instead of awaiting Async completions, your AppDoman and process will end. A proper way to address this would be to use Stephen Cleary's AsyncContext to establish full Async support in your single-threaded console program. But for simpler cases, like waiting for a keypress, installing a full trampoline may be overkill.

The example below would be for a console program used in some kind of iterative batch file. In this case, when the program is done with its work, normally it should exit without requiring a keypress, and then we allow an optional key press to prevent the app from exiting. We can pause the cycle to examine things, possibly resuming, or use the pause as a known 'control point' at which to cleanly break out of the batch file.

К событиям клавиатуры можно отнести следующие события:

Возникает при нажатии клавиши

Возникает при нажатии клавиши

Возникает при освобождении клавиши

Возникает при освобождении клавиши

Возникает при получении элементом текстового ввода (генерируется не только клавиатурой, но и стилусом)

Возникает при получении элементом текстового ввода

Большинство событий клавиатуры (KeyUp/PreviewKeyUp, KeyDown/PreviewKeyDown) принимает в качестве аргумента объект KeyEventArgs, у которого можно отметить следующие свойства:

Key позволяет получить нажатую или отпущенную клавишу

SystemKey позволяет узнать, нажата ли системная клавиша, например, Alt

KeyboardDevice получает объект KeyboardDevice, представляющее устройство клавиатуры

IsRepeat указывает, что клавиша удерживается в нажатом положении

IsUp и IsDown указывает, была ли клавиша нажата или отпущена

IsToggled указывает, была ли клавиша включена - относится только к включаемым клавишам Caps Lock, Scroll Lock, Num Lock

Например, обработаем событие KeyDown для текстового поля и выведем данные о нажатой клавише в текстовый блок:

А в файле кода пропишем обработчик TextBox_KeyDown :

Здесь в текстовый блок добавляется текстовое представление нажатой клавиши в текстовом поле:

События клавиатуры в WPF

Правда, в данном случае реальную пользу от текстового представления мы можем получить только для алфавитно-цифровых клавиш, в то время как при нажатии специальных клавиш или кавычек будут добавляться их полные текстовые представления, например, для кавычек - OemQuotes.

Если нам надо отловить нажатие какой-то опредленной клавиши, то мы можем ее проверить через перечисление Key :

Объект KeyboardDevice позволяет нам получить ряд дополнительных данных о собыиях клавиатуры через ряд свойств и методов:

Modifiers позволяет узнать, какая клавиша была нажата вместе с основной (Ctrl, Shift, Alt)

IsKeyDown() определяет, была ли нажата определенная клавиша во время события

IsKeyUp() позволяет узнать, была ли отжата определенная клавиша во время события

IsKeyToggled() позволяет узнать, была ли во время события включена клавиша Caps Lock, Scroll Lock или Num Lock

GetKeyStates() возвращает одно из значений перечисления KeyStates, которое указывает на состояние клавиши

Пример использования KeyEventArgs при одновременном нажатии двух клавиш Shift и F1:

События TextInput/PreviewTextInput в качестве параметра принимают объект TextCompositionEventArgs . Из его свойств стоит отметить, пожалуй, только свойство Text , которое получает введенный текст, именно текст, а не текстовое представление клавиши. Для этого добавим к текстовому полю обработчик:

И определим обработчик в файле кода:

Причем в данном случае я обрабатываю именно событие PreviewTextInput, а не TextInput, так как элемент TextBox подавляет событие TextInput, и вместо него генерирует событие TextChanged. Для большинства других элементов управления, например, кнопок, событие TextInput прекрасно срабатывает.

Валидация текстового ввода

События открывают нам большой простор для валидации текстового ввода. Нередко при вводе используются те или иные ограничения: нельзя вводить цифровые символы или, наоборот, можно только цифровые и т.д. Посмотрим, как мы можем провети валидацию ввода. К примеру, возьмем ввод номера телефона. Сначала зададим обработку двух событий в xaml:

И определим в файле кода обработчики:

Для валидации ввода нам надо использовать обработчики для двух событий - PreviewKeyDown и PreviewTextInput. Дело в том, что нажатия не всех клавиш PreviewTextInput обрабатывает. Например, нажатие на клавишу пробела не обрабтывается. Поэтому также применяется обработка и PreviewKeyDown.

Сами обработчики проверяют ввод и если ввод соответствует критериям, то он отклоняется с помощью установки e.Handled = true . Тем самым мы говорим, что событие обработано, а введенные текстовые сиволы не будут появляться в текстовом поле. Конкретно в данном случае пользователь может вводить только цифровые символы и пробел в соответствии с форматом телефонного номера.

Элементы управления представляют собой визуальные классы, которые получают введенные пользователем данные и могут инициировать различные события. Все элементы управления наследуются от класса Control и поэтому имеют ряд общих свойств:

Anchor : Определяет, как элемент будет растягиваться

BackColor : Определяет фоновый цвет элемента

BackgroundImage : Определяет фоновое изображение элемента

ContextMenu : Контекстное меню, которое открывается при нажатии на элемент правой кнопкой мыши. Задается с помощью элемента ContextMenu

Cursor : Представляет, как будет отображаться курсор мыши при наведении на элемент

Dock : Задает расположение элемента на форме

Enabled : Определяет, будет ли доступен элемент для использования. Если это свойство имеет значение False, то элемент блокируется.

Font : Устанавливает шрифт текста для элемента

ForeColor : Определяет цвет шрифта

Location : Определяет координаты верхнего левого угла элемента управления

Name : Имя элемента управления

Size : Определяет размер элемента

Width : ширина элемента

Height : высота элемента

TabIndex : Определяет порядок обхода элемента по нажатию на клавишу Tab

Tag : Позволяет сохранять значение, ассоциированное с этим элементом управления

Кнопка

Наиболее часто используемым элементом управления является кнопка. Обрабатывая событие нажатия кнопки, мы может производить те или иные действия.

При нажатии на кнопку на форме в редакторе Visual Studio мы по умолчанию попадаем в код обработчика события Click , который будет выполняться при нажатии:

Оформление кнопки

Чтобы управлять внешним отображением кнопки, можно использовать свойство FlatStyle. Оно может принимать следующие значения:

Flat - Кнопка имеет плоский вид

Popup - Кнопка приобретает объемный вид при наведении на нее указателя, в иных случаях она имеет плоский вид

Standard - Кнопка имеет объемный вид (используется по умолчанию)

System - Вид кнопки зависит от операционной системы

Изображение на кнопке

Как и для многих элементов управления, для кнопки можно задавать изображение с помощью свойства BackgroundImage. Однако мы можем также управлять размещением текста и изображения на кнопки. Для этого надо использовать свойство TextImageRelation . Оно приобретает следующие значения:

Overlay : текст накладывается на изображение

ImageAboveText : изображение располагается над текстом

TextAboveImage : текст располагается над изображением

ImageBeforeText : изображение располагается перед текстом

TextBeforeImage : текст располагается перед изображением

Например, установим для кнопки изображение. Для этого выберем кнопку и в окне Свойств нажмем на поле Image (не путать с BackgroundImage). Нам откроется диалоговое окно установи изображения:

Установка изображения для кнопки

В этом окне выберем опцию Local Resource и нажмем на кнопку Import , после чего нам откроется диалоговое окно для выбора файла изображения.

После выбора изображения мы можем установить свойство ImageAlign , которое управляет позиционированием изображения на кнопке:

ImageAlign

Нам доступны 9 вариантов, с помощью которых мы можем прикрепить изображение к определенной стороне кнопки. Оставим здесь значение по умолчанию - MiddleCenter , то есть позиционирование по центру.

Затем перейдем к свойству TextImageRelation и установим для него значение ImageBeforeText . В итоге мы получим кнопку, где сразу после изображения идет надпись на кнопке:

Кнопка с изображением в Windows Forms

Клавиши быстрого доступа

При работе с формами при использовании клавиатуры очень удобно пользоваться клавишами быстрого доступа. При нажатии на клавиатуре комбинации клавиш At+некоторый символ, будет вызываться определенная кнопка. Например, зададим для некоторой кнопки свойство Text равное &Аватар . Первый знак - амперсанд - определяет ту букву, которая будет подчеркнута. В данном случае надпись будет выглядеть как А ватар. И теперь чтобы вызвать событие Click, нам достаточно нажать на комбинацию клавиш Alt+А.

Кнопки по умолчанию

Форма, на которой размещаются все элементы управления, имеет свойства, позволяющие назначать кнопку по умолчанию и кнопку отмены.

Так, свойство формы AcceptButton позволяет назначать кнопку по умолчанию, которая будет срабатывать по нажатию на клавишу Enter.

Аналогично работает свойство формы CancelButton , которое назначает кнопку отмены. Назначив такую кнопку, мы можем вызвать ее нажатие, нажав на клавишу Esc.

Читайте также: