0

Greetings,

I want to write code that executes within an event handler, inside a WPF Windows application, that can detect a keypress, specifically an "Escape" character keypress, within a processing loop. This will allow the user to escape from processing. I realize this may be accomplished with some kind of multi-threaded approach, but the problem seems so simple I wondered if it might be accomplished as follows:

// Attempt 1: See if Keyboard static IsKeyDown method detects key presses while executing.
// Note that this was not successful. The Keyboard states do not appear to be updated during processing.

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

So, on to attempt #2. I saw some articles and samples using the Pinvoke "GetKeyboardState" method. I'm not sure I used the method correctly, but here is my attempt. It is a bit clumsy to refer to a Windows.Forms enumeration in a WPF application, but it seems like it could work.

// Attempt 2: Use Pinvoke GetKeyboardState method.
// So far, I've been unsuccessful with this as well, but I'm not sure my usage is correct.

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

But unfortunately, I'm not seeing any change in the keyboard states as this executes. I also played around a little with calls to the Dispatcher to see if I could get the keyboard information to refresh during processing, but I have not been successful with any technique.

I'm out of ideas. Can someone propose something? Thank you in advance for your assistance.

  • David

2 Answers2

3

Something like this:

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

The important point is that the method that does the work is executed in a separate thread so the main thread can process user input (key strokes).

Pavlo Glazkov
  • 20,498
  • 3
  • 58
  • 71
  • I think both of the above answers are quite helpful. I just have to accept that a multi-threaded approach is the only way to accomplish this. Perhaps because of my own hesitation with complications that occur from multi-threading, I had hoped to achieve a way to check the keyboard state without returning control to Windows in the UI thread. We are modeling our solution after the checked example. I will post if we have any more significant complications. Thank you for you all for your great assist. – Code-an the Barbarian Jan 25 '11 at 20:56
1

You can not detect a key event while you are blocking WPF by executing a very long loop. You must use a multithreaded approach or you have to split the loop.

Using a BackgroundWorker is an easy way to let WPF continue handling the frontend while executing the loop.

    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();
    }
matthias.lukaszek
  • 2,200
  • 1
  • 23
  • 33