Обнаружение нажатия клавиш во время обработки WPF - PullRequest
0 голосов
/ 25 января 2011

Привет,

Я хочу написать код, который выполняется в обработчике событий внутри приложения WPF Windows, который может обнаружить нажатие клавиши, в частности нажатие клавиши «Escape», в цикле обработки. Это позволит пользователю избежать обработки. Я понимаю, что это может быть достигнуто с помощью какого-то многопоточного подхода, но проблема кажется настолько простой, что я подумал, можно ли это сделать следующим образом:

// Попытка 1: проверьте, обнаруживает ли статический метод KeyK IsKeyDown клавиатур обнаружение нажатий клавиш во время выполнения.
// Обратите внимание, что это не удалось. Состояния клавиатуры не обновляются во время обработки.

        bool iskeypressed = false;
        while (!iskeypressed)
        {
            System.Threading.Thread.Sleep(1000);
            if (Keyboard.IsKeyDown(Key.Enter))
                iskeypressed = true;  
        }

Итак, попытка № 2. Я видел несколько статей и примеров, используя метод Pinvoke «GetKeyboardState». Я не уверен, что использовал метод правильно, но вот моя попытка. Немного неуклюже ссылаться на перечисление Windows.Forms в приложении WPF, но кажется, что оно может работать.

// Попытка 2: использовать метод Pinvoke GetKeyboardState.
// До сих пор у меня тоже не получилось, но я не уверен, что мое использование правильное.

        bool iskeypressed = false;
        while (!iskeypressed)
        {
            System.Threading.Thread.Sleep(1000);
            if (isEscapePressed()) 
                iskeypressed = true;  
        }
    }


    [DllImport("user32.dll")] public static extern int GetKeyboardState(byte[] lpKeyState);
    private bool isEscapePressed()
    {
        byte[] keyboardState = new byte[255];
        int keystate = GetKeyboardState(keyboardState);

        if (keyboardState[(int)System.Windows.Forms.Keys.Escape] == 128)
            return true;
        else
            return false; 
    }    

Но, к сожалению, я не вижу каких-либо изменений в состояниях клавиатуры, поскольку это выполняется. Я также немного поигрался с обращениями к Диспетчеру, чтобы узнать, смогу ли я обновить информацию клавиатуры во время обработки, но я не добился успеха ни с какой техникой.

У меня нет идей. Может кто-то что-то предложить? Спасибо заранее за помощь.

  • David

Ответы [ 2 ]

3 голосов
/ 25 января 2011

Примерно так:

private bool IsCancelled { get; set; }

private void OnButtonClick(object sender, EventArgs e)
{
   Action doWorkDelegate = DoWork;

   doWorkDelegate.BeginInvoke(null, null);
}

protected override void OnKeyDown(KeyEventArgs e) {
    if (e.Key == Key.Escape) {
        IsCancelled = true;
        e.Handled = true;
    } else {
        base.OnKeyDown(e);
    }
}

private void DoWork()
{
   IsCancelled  = false;
   while (!IsCancelled)
   {
       System.Threading.Thread.Sleep(1000);
   }
}

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

1 голос
/ 25 января 2011

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

Использование BackgroundWorker - это простой способ позволить WPF продолжать обработку внешнего интерфейса во время выполнения цикла.

    private BackgroundWorker bw;

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (bw != null)
            return;

        bw = new BackgroundWorker();
        bw.WorkerSupportsCancellation = true;
        bw.WorkerReportsProgress = true;

        bw.DoWork += (senderBw, eBw) =>
        {
            for (int i = 0; i < 100; i++)
            {
                Thread.Sleep(1000);

                bw.ReportProgress(i);

                if (eBw.Cancel)
                    return;
            }
        };
        bw.ProgressChanged += (senderBw, eBw) =>
        {
            //TODO set progressbar to eBw.ProgressPercentage
        };
        bw.RunWorkerCompleted += (senderBw, eBw) =>
        {
            this.bw = null;
            //TODO frontend stuff (hide progressbar etc)
        };

        bw.RunWorkerAsync();
    }

    private void MainWindow_KeyDown(object sender, KeyEventArgs e)
    {
        if (this.bw != null && this.bw.IsBusy && e.Key == Key.Escape)
            this.bw.CancelAsync();
    }
...