Keyboard.GetKeyStates falsch positiv, woher weiß ich eigentlich, ob eine Taste gedrückt wurde?

Es scheint einen Weg für @ zu gebKeyboard.GetKeyStates, um falsch gedrückte Tasten zurückzugeben, z. B. fürKeyboard.GetKeyStates(Key.NumPad2) zurückgebenDown, Toggled auch wenn nicht gedrückt.

Ich konnte dies auf einer sehr einfachen WPF-App reproduzieren, die KeyUp-Ereignisse abfängt. Der Fehler kann auf folgende Weise reproduziert werden:

press NumPad2press LShiftrelease NumPad2release LShift

Ab dann ergibt das Überprüfen des Status für NumPad2 immerDown, Toggled, bis Sie drücken und wieder loslassen.

Nicht sicher, dass es wichtig ist, aber ich benutze die englische UK Extended-Tastatur aufWindows 8.1 x64

Der Grund scheint zu sein, dass LShift-NumPad2 tatsächlich der Nach-Unten-Taste entspricht (sinnvoll), aber Windows scheint nicht zu bemerken, dass dies bedeutet, dass NumPad2 nicht mehr gedrückt wird.

Meine Test-App fängt einfach KeyDown und KeyUp ab und zeigt mir die KeyStates-Änderungen für jedes Ereignis sowie eine Liste der KeyStates für die gesamte Tastatur nach jedem Ereignis an (ich vergleiche sie mit dem Zustand, in dem die Anwendung gestartet wurde, um die KeyStates nicht zu verschmutzen) Ausgabe mit dem Status der NumLock-Tasten und anderer.

Dies ist die Ausgabe, die ich mit dem vorherigen Test erhalte:

MainWindow_OnKeyDown NumPad2: Down, Toggled -> Down, Toggled
KeyStates:
    NumPad2: Down, Toggled

MainWindow_OnKeyDown LeftShift: None -> Down, Toggled
KeyStates:
    NumPad2: Down, Toggled
    LeftShift: Down, Toggled

MainWindow_OnKeyUp LeftShift: Down, Toggled -> Toggled
KeyStates:
    NumPad2: Down, Toggled
    LeftShift: Toggled

MainWindow_OnKeyUp Down: None -> None
KeyStates:
    NumPad2: Down, Toggled
    LeftShift: Toggled

MainWindow_OnKeyUp LeftShift: Toggled -> None
KeyStates:
    NumPad2: Down, Toggled

So wie Sie sehen können, wird die NumPad2-Taste nach den Schritten 3 und 4 als gedrückt angezeigt, obwohl ich sie in Schritt 3 losgelassen habe.

Hier finden Sie den vollständigen Code für die XAML und den Code dahinter, falls Sie diesen direkt in ein neues Projekt kopieren / einfügen und in Aktion sehen möchten:

<Window x:Class="WpfKeyboardTester.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        Loaded="MainWindow_OnLoaded"
        KeyDown="MainWindow_OnKeyDown"
        KeyUp="MainWindow_OnKeyUp"
        WindowStartupLocation="CenterScreen">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ScrollViewer
            Grid.Row="0"
            Name="ScrollViewer"
            x:FieldModifier="private">
            <TextBox
                Name="TextBox"
                IsReadOnly="True"
                x:FieldModifier="private"/>
        </ScrollViewer>
        <Button
            Grid.Row="1"
            Click="Clear_OnClick">
            Clear
        </Button>
    </Grid>
</Window>

Un

   public partial class MainWindow
   {
      private Dictionary<Key, KeyStates> _initialKeyStates;
      private Dictionary<Key, KeyStates> _keyStates;
      private Key _previousKeyDown;
      private Key _previousKeyUp;

      public MainWindow()
      {
         InitializeComponent();
      }

      private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
      {
         _keyStates = GetKeyStates();
         _initialKeyStates = _keyStates;
      }

      private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
      {
         if (e.Key != _previousKeyDown)
         {
            AppendKeyEventDescription("MainWindow_OnKeyDown", e);
            _previousKeyDown = e.Key;            
         }
      }

      private void MainWindow_OnKeyUp(object sender, KeyEventArgs e)
      {
         if (e.Key != _previousKeyUp)
         {
            AppendKeyEventDescription("MainWindow_OnKeyUp", e);
            _previousKeyUp = e.Key;
         }
      }

      private void Clear_OnClick(object sender, RoutedEventArgs e)
      {
         TextBox.Text = string.Empty;
      }

      private static Dictionary<Key, KeyStates> GetKeyStates()
      {
         return Enum.GetValues(typeof (Key))
            .Cast<Key>()
            .Distinct()
            .Where(x => x != Key.None)
            .ToDictionary(
               x => x,
               Keyboard.GetKeyStates);
      }

      private void AppendKeyEventDescription(string eventName, KeyEventArgs e)
      {
         if (TextBox.Text != string.Empty)
            TextBox.Text += "\n\n";
         TextBox.Text +=
            eventName + " " + e.Key + ": " + _keyStates[e.Key] + " -> " + e.KeyStates;

         _keyStates = GetKeyStates();

         var changedKeys = _keyStates.Keys
            .Where(key => _keyStates[key] != _initialKeyStates[key])
            .ToArray();

         if (changedKeys.Any())
         {
            TextBox.Text += changedKeys.Aggregate(
               "\nKeyStates:",
               (text, key) => text + "\n\t" + key + ": " + _keyStates[key]);
         }
      }
   }

Ich habe verschiedene andere Ansätze mit Win32-API-Aufrufen untersucht:

GetKeyStateGetKeyboardStateGetAsyncKeyStates

Und sie haben alle genau das gleiche Problem (nicht überraschend, da ich annehme, dass ihr Innenleben sowieso mit ihrem .net-Äquivalent Keyboard.GetKeyStates geteilt wird).

Nun kann ich jeden Moment feststellen, ob die NumPad2-Taste wirklich gedrückt ist ...

Antworten auf die Frage(1)

Ihre Antwort auf die Frage