Обнаружение нажатия клавиши ввода во время обработки WPF

Привет,

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

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

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

Итак, попытка №2. Я видел несколько статей и примеров, использующих метод GetKeyboardState в Pinvoke. Я не уверен, что правильно использовал этот метод, но вот моя попытка. Немного неуклюже ссылаться на перечисление 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; 
    }    

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

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

  • Дэйвид

person Code-an the Barbarian    schedule 25.01.2011    source источник


Ответы (2)


Что-то вроде этого:

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);
   }
}

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

person Pavlo Glazkov    schedule 25.01.2011
comment
Я думаю, что оба приведенных выше ответа весьма полезны. Я просто должен признать, что многопоточный подход — единственный способ добиться этого. Возможно, из-за моих собственных сомнений по поводу осложнений, возникающих из-за многопоточности, я надеялся найти способ проверить состояние клавиатуры, не возвращая управление Windows в потоке пользовательского интерфейса. Мы моделируем наше решение по проверенному примеру. Я опубликую, если у нас будут более серьезные осложнения. Спасибо вам всем за вашу большую помощь. - person Code-an the Barbarian; 25.01.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();
    }
person matthias.lukaszek    schedule 25.01.2011