question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Gathering keyboard input doesn't work (winui 3)

See original GitHub issue

Describe the bug

I simply can’t believe that this is an issue in 2022:

There’s simply no reliable way to capture keyboard in winui – I have no idea what to do.

I won’t reiterate what Gavin Williams already said in #3986 – we don’t have a CoreWindow in WinUI 3, and I don’t see any way to reliably capture keys.

Steps to reproduce the bug

I have a DataGrid and I want to capture keyboard input for it. Obviously, since I don’t have access to CoreWindow, I tried to capture its KeyDown or PreviewKeyDown Of course it doesn’t work.

Then I tried to capture the parent’s Keydown:

var grid = Parent as Grid;
Debug.Assert(grid != null);

grid.IsTabStop = true;
grid.IsHitTestVisible = true;
grid.Background = new SolidColorBrush(Colors.Transparent);
grid.PreviewKeyDown += DecentDataGrid_KeyDown;

This doesn’t work either. There’s simply no way to capture this. How am I supposed to create a half decent app when I can’t even handle an INS hotkey?

LATER EDIT: Turns out the issue with the datagrid not handling KeyDown happens when it’s placed on a pivot.

Expected behavior

  1. There should be a way to globally capture keyboard, like in UWP
  2. When capturing KeyDown, this should work reliably (see below, when Datagrid is on a Pivot)

Screenshots

No response

NuGet package version

WinUI 3 - Windows App SDK 1.0.4

Windows app type

  • UWP
  • Win32

Device form factor

Desktop

Windows version

Windows 10 (21H1): Build 19043

Additional context

No response

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:2
  • Comments:13 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
MYPOSConnectcommented, Jul 7, 2022

I got it going, thanks to Castorix and some digging around on Google. It needs some tidying up after being hacked together but seems to work ok.

  1. Put this class WinAPI.cs in Platforms/Windows folder
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace YourApp { 

    public class WinAPI
    {
        public delegate void HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern uint GetCurrentThreadId();

        [DllImport("user32.dll")]
        public static extern int MapVirtualKey(uint uCode, uint uMapType);

     [DllImport("user32.dll")]
        public static extern int ToUnicode(uint virtualKeyCode, uint scanCode,
    byte[] keyboardState,
    [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)]
    StringBuilder receivingBuffer,
    int bufferSize, uint flags);

        public const int WH_MIN = (-1);
        public const int WH_MSGFILTER = (-1);
        public const int WH_JOURNALRECORD = 0;
        public const int WH_JOURNALPLAYBACK = 1;
        public const int WH_KEYBOARD = 2;
        public const int WH_GETMESSAGE = 3;
        public const int WH_CALLWNDPROC = 4;
        public const int WH_CBT = 5;
        public const int WH_SYSMSGFILTER = 6;
        public const int WH_MOUSE = 7;
        public const int WH_HARDWARE = 8;
        public const int WH_DEBUG = 9;
        public const int WH_SHELL = 10;
        public const int WH_FOREGROUNDIDLE = 11;
        public const int WH_CALLWNDPROCRET = 12;
        public const int WH_KEYBOARD_LL = 13;
        public const int WH_MOUSE_LL = 14;
        public const int WH_MAX = 14;
        public const int WH_MINHOOK = WH_MIN;
        public const int WH_MAXHOOK = WH_MAX;

        public const int KF_EXTENDED = 0x0100;
        public const int KF_DLGMODE = 0x0800;
        public const int KF_MENUMODE = 0x1000;
        public const int KF_ALTDOWN = 0x2000;
        public const int KF_REPEAT = 0x4000;
        public const int KF_UP = 0x8000;

        internal static int HIWORD(IntPtr wParam)
        {
            return (int)((wParam.ToInt64() >> 16) & 0xffff);
        }

        internal static int LOWORD(IntPtr wParam)
        {
            return (int)(wParam.ToInt64() & 0xffff);
        }

    }
}

  1. Add this using to Platform/Windows/App.xaml.cs:
using static YourApp.WinAPI;
  1. Add these as properties in Platforms/Windows/App.xaml.cs
 private static int m_hHook = 0;
 private HookProc m_HookProcedure;
  1. Add these methods to same file
  protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs e)
    {
        base.OnLaunched(e);
        m_HookProcedure = new HookProc(HookProcedure);
        m_hHook = SetWindowsHookEx(WH_KEYBOARD, m_HookProcedure, (IntPtr)0, (int)GetCurrentThreadId());

    }
    
    private void HookProcedure(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode < 0) return;

        bool shift = (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down));

        string s = GetCharFromKey((uint)wParam, shift, false);

        if (s == "") return;

        Debug.WriteLine(s);

    }

    static string GetCharFromKey(uint key, bool shift, bool altGr)
    {
        var buf = new StringBuilder(256);
        var keyboardState = new byte[256];
        if (shift)
            keyboardState[(int)VirtualKey.Shift] = 0xff;
        if (altGr)
        {
            keyboardState[(int)VirtualKey.Control] = 0xff;
            keyboardState[(int)VirtualKey.Menu] = 0xff;
        }
        WinAPI.ToUnicode(key, 0, keyboardState, buf, 256, 0);

        if (buf.Length == 0) return "";
        return buf[0].ToString();
    }
    

0reactions
applefanboiscommented, Jul 17, 2023

Can’t believe we are here because Microsoft forgot that a PC use a mouse and … a keyboard.

Read more comments on GitHub >

github_iconTop Results From Across the Web

WinUI 3 keyboard layout for input from SwapChainPanel
I'm working on a WinUI 3 - C++/winRT - desktop application. ... With KeyDown and KeyUp on the SwapChainPanel, I can get raw...
Read more >
Close button freezes by injected input in WinUI3?
If it freezes and i open the task manager, the window closes immediately ... It seems, as if the close button doesn't accept...
Read more >
Keyboard events - Windows apps
Respond to keystroke actions from hardware or software keyboards in your apps using both keyboard and class event handlers.
Read more >
Uno support for user inputs
They won't bubble in managed code. iOS: KeyDown & KeyUp routed events are generated from only a TextBox . Only character-related keyboard events...
Read more >
Accidentally set off mode that ignores keyboard input ...
The enter key did not work, but space appeared to act as enter. When I ended the Notepad++ process, everything returned to normal....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found