C# WPF: Как правильно вызывать программное нажатие горячей клавиши?
В WPF-приложении использую модуль NHotkey для обработки горячих клавиш.
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace test
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
NHotkey.Wpf.HotkeyManager.Current.AddOrReplace("Test", Key.G, ModifierKeys.Alt, Test);
}
async private static void Test(object sender, EventArgs e)
{
await Task.Delay(300);
KeyboardManager.ModifiedKeyStroke(Key.A, ModifierKeys.Control);
}
}
}
Благодаря 2 строке в конструкторе MainWindow(), при нажатии сочетания ALT+G вызывается метод Test(), который программно нажимает сочетание клавиш CTRL+A для выделения текста в любом поле ввода.
Проблема:
Если убрать из кода строку await Task.Delay(300);, то текст в поле ввода не будет выделяться от программного нажатия CTRL+A по причине того, что метод вызывается моментально, еще в тот момент, когда кнопка ALT (из сочетания ALT+G) не была отпущена.
В итоге получается сочетание клавиш ALT+CTRL+A, которое не выделяет текст из-за лишней клавиши ALT.
Временное решение:
Чтобы решить проблему, я добавил в код метода Test() задержку в 300 мс, чтобы было время на отпускание кнопки. В этом случае, если ALT была отпущена в течение 300 мс после нажатия, выделение текста от CTRL+A срабатывает, но это не является нормальным решением проблемы.
Кроме этого мне приходило на ум как-то "отжимать" все нажатые клавиши перед вызовом метода, но правильно ли это?
В коде используется класс KeyboardManager, вот код от него: https://pastebin.com/f1xmZUW0
Ответы (1 шт):
Я понял, вам кажется надо подождать когда все кнопки на клаве будут отпущены пользователем прежде чем запускать автоматику.
private readonly Key[] keys = Enum.GetValues<Key>().Where(key => key != Key.None).ToArray();
private async Task WaitForKeysReleaseAsync()
{
int threshold = 0;
while (Keyboard.Modifiers != ModifierKeys.None || keys.Any(key => Keyboard.IsKeyDown(key)))
{
if (threshold++ == 100) // ~5 секунд
throw new TimeoutException("Превышено время ожидания освобождения клавиатуры или произошло залипание клавиш");
await Task.Delay(50);
}
}
Ну и вызывать так
private async Task TestAsync(object sender, EventArgs e)
{
await WaitForKeysReleaseAsync();
KeyboardManager.ModifiedKeyStroke(Key.A, ModifierKeys.Control);
}
try
{
await TestAsync();
// здесь код, который надо выполнить, если всё прошло хорошо
}
catch (TimeoutException ex)
{
// ... ex.Message, комбинация клавиш не была выполнена
}
Если клава через 5 секунд не освободилась, значит явно что-то пошло не так. К сожалению, в моей практике такие случаи были, приходилось даже перезагружать комп, потому что клаве было реально плохо, но это на самом деле редкость. В общем такой вариант исключать нельзя, потому что приложение просто может не дождаться освобождения клавы и зависнуть насовсем.