tags:

views:

39

answers:

3

I have some code which is (supposed to be) capturing keystrokes. The top level window has a

Keyboard.PreviewKeyDown="Window_PreviewKeyDown"

clause and the backing CS file contains:

private void Window_PreviewKeyDown(object sender, KeyEventArgs e) {
    if (e.KeyboardDevice.Modifiers == ModifierKeys.Control) {
        switch (e.Key) {
            case Key.L:
                btnPrev_Click(sender, new RoutedEventArgs());
                e.Handled = true;
                break;
            case Key.R:
                btnNext_Click(sender, new RoutedEventArgs());
                e.Handled = true;
                break;
        }
    }
}

Now that works fine, both CTRLL and CTRLR call the relevant functions.

As soon as I change the modifer check to use ModifierKeys.Alt, it stops working. In other words, neither ALTL and ALTR call the functions.

What am I missing here?

A: 

The problem may be that you're mixing several classes/namespaces together.

The e.KeyboardDevice.Modifiers property is of the System.Windows.Forms.Keys (Windows Forms) type, while ModifierKeys.Control or ModifierKeys.Alt, for that matter, is a WPF class.

Using the following code would be much less error prone:

if(e.Alt) { 
  ...
}
arul
-1 that is misleading and incorrect. The question is clearly tagged WPF and a WinForms to WPF enum comparison would not even compile. There is no evidence of any WinForms types being used here.
Josh Einstein
arul, thanks for trying to help out but the `KeyEventArgs` `e` doesn't appear to _have_ an `Alt` member. Can't comment much more on Josh's remarks since I'm a relative newbie at C#/WPF but I'm pretty certain I'm not using any WinForms stuff.
paxdiablo
Oops, sorry, somehow my mind still hasn't escaped from the WinForms land.
arul
+2  A: 

In case of an Alt modifier, e.Key returns Key.System and the real key is in e.SystemKey. You can use the following piece of code to always get the correct pressed key:

Key key = (e.Key == Key.System ? e.SystemKey : e.Key);
Julien Lebosquain
It was a toss-up between you and RayB but you just pipped him at the post by virtue of providing the "minimal code change" solution :-) Thanks.
paxdiablo
+3  A: 

The trouble is that when Alt is held down your KeyEventArgs has:

Key = Key.System
SystemKey = the real key

so when checking for Alt you need to use e.SystemKey instead of e.Key, like this:

if (e.KeyboardDevice.Modifiers == ModifierKeys.Alt) {   
    switch (e.SystemKey) { 
      ...

Explanation

Under Windows, the "Alt" key is handled specially. When the Alt key itself is pressed or another key is pressed while the Alt key is held down, it is considered a "System" keypress. "System" keypresses are handled differently than regular keypresses in many ways.

It all starts out when Windows passes the keypress to your application. Normal key down events generate a WM_KEYDOWN, but if the Alt key is pressed it generates a WM_SYSKEYDOWN. By the same token a WM_KEYUP is translated into a WM_SYSKEYUP.

Throughout Windows, including in WPF, the special handling of the Alt key is used with MenuItems, Buttons and Labels that include "access text". For example, if a button has content of "Say _Hi", then presing Alt-H will be treated as a a button click.

When the Alt key is down, letters come in as three pairs of events: KeyDown, KeyUp and TextInput, each with their associated preview versions. The primary differences here are:

  • The KeyDown and KeyUp events have their Key property set to "Key.System" rather than the actual key that was pressed, and the SystemKey set to the actual key pressed.
  • The TextInput event is passed normally but then handled as an AccessKey if it is not consumed
Ray Burns
Should that second code block be `ModifierKeys.Alt`?
paxdiablo
Thanks, Ray, the explanation was an added bonus so +1 for that.
paxdiablo
Excellent explanation! +1 for you Sir
Arcturus