How to Capture the '#' Character on Different Locale Keyboards in Wpf/C#

how to capture the '#' character on different locale keyboards in WPF/C#?

The function below, GetCharFromKey(Key key) will do the trick.

It uses a series of win32 calls to decode the key pressed:

  1. get the virtual key from WPF key

  2. get the scan code from the virtual key

  3. get your unicode character

This old post describes it in a bit more detail.

      public enum MapType : uint
{
MAPVK_VK_TO_VSC = 0x0,
MAPVK_VSC_TO_VK = 0x1,
MAPVK_VK_TO_CHAR = 0x2,
MAPVK_VSC_TO_VK_EX = 0x3,
}

[DllImport("user32.dll")]
public static extern int ToUnicode(
uint wVirtKey,
uint wScanCode,
byte[] lpKeyState,
[Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)]
StringBuilder pwszBuff,
int cchBuff,
uint wFlags);

[DllImport("user32.dll")]
public static extern bool GetKeyboardState(byte[] lpKeyState);

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

public static char GetCharFromKey(Key key)
{
char ch = ' ';

int virtualKey = KeyInterop.VirtualKeyFromKey(key);
byte[] keyboardState = new byte[256];
GetKeyboardState(keyboardState);

uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
StringBuilder stringBuilder = new StringBuilder(2);

int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
switch (result)
{
case -1:
break;
case 0:
break;
case 1:
{
ch = stringBuilder[0];
break;
}
default:
{
ch = stringBuilder[0];
break;
}
}
return ch;
}

Global keyboard capture in C# application

Stephen Toub wrote a great article on implementing global keyboard hooks in C#:

using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;

public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}

private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}

private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);

private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine((Keys)vkCode);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}

How can i generate a localized keyboard shortcut in C# / WPF?

I had the same problem myself and checked the WPF source code. Sadly, strings like "Ctrl" are in fact hardcoded.

So my idea was to simply use the WindowsForms classes for displaying shortcut strings. You can do this with the following code (you could use this for a derived RoutedCommand class that automatically replaces all shortcut keys with a new display string):

private void LocalizeShortcuts()
{
foreach (KeyGesture keyGuesture in this.InputGestures.OfType<KeyGesture>().ToArray())
{
this.InputGestures.Remove(keyGuesture);

System.Windows.Forms.Keys formsKey = (Keys)KeyInterop.VirtualKeyFromKey(keyGuesture.Key);
if ((keyGuesture.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
{
formsKey |= Keys.Alt;
}

if ((keyGuesture.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
formsKey |= Keys.Control;
}

if ((keyGuesture.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
{
formsKey |= Keys.Shift;
}

string keyDisplayString = TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString(formsKey);
this.InputGestures.Add(new KeyGesture(keyGuesture.Key, keyGuesture.Modifiers, keyDisplayString));
}
}

How to show button is pressed in UI for combination keys

This works for me:

//Make the button to have "pressed" feel when the corresponding key is pressed

private void NumDisplayBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift))
{
if (e.Key == Key.D3)
HashBtn.Style = (Style) FindResource("PressedButtonStyle");

if (e.Key == Key.D8)
StarBtn.Style = (Style) FindResource("PressedButtonStyle");
}

switch (e.Key)
{
case Key.D0:
case Key.NumPad0:
ZeroBtn.Style = (Style) FindResource("PressedButtonStyle");
break;
case Key.D1:
case Key.NumPad1:
OneBtn.Style = (Style) FindResource("PressedButtonStyle");
break;
case Key.D2:
case Key.NumPad2:
TwoBtn.Style = (Style) FindResource("PressedButtonStyle");
break;
case Key.D3:
case Key.NumPad3:
{
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
ThreeBtn.Style = (Style) FindResource("ButtonStyle4");

else
ThreeBtn.Style = (Style) FindResource("PressedButtonStyle");

break;
}
case Key.D4:
case Key.NumPad4:
FourBtn.Style = (Style) FindResource("PressedButtonStyle");
break;
case Key.D5:
case Key.NumPad5:
FiveBtn.Style = (Style) FindResource("PressedButtonStyle");
break;
case Key.D6:
case Key.NumPad6:
SixBtn.Style = (Style) FindResource("PressedButtonStyle");
break;
case Key.D7:
case Key.NumPad7:
SevenBtn.Style = (Style) FindResource("PressedButtonStyle");
break;
case Key.D8:
case Key.NumPad8:
{
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
EightBtn.Style = (Style) FindResource("ButtonStyle4");

else
EightBtn.Style = (Style) FindResource("PressedButtonStyle");

break;
}
case Key.D9:
case Key.NumPad9:
NineBtn.Style = (Style) FindResource("PressedButtonStyle");
break;
default:
break;
}
}

//Return back to its original style

private void NumDisplayBox_PreviewKeyUp(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.D0:
case Key.NumPad0:
ZeroBtn.Style = (Style) FindResource("ButtonStyle4");
break;
case Key.D1:
case Key.NumPad1:
OneBtn.Style = (Style) FindResource("ButtonStyle4");
break;
case Key.D2:
case Key.NumPad2:
TwoBtn.Style = (Style) FindResource("ButtonStyle4");
break;
case Key.D3:
case Key.NumPad3:
ThreeBtn.Style = (Style) FindResource("ButtonStyle4");
HashBtn.Style = (Style) FindResource("ButtonStyle4");
break;
case Key.D4:
case Key.NumPad4:
FourBtn.Style = (Style) FindResource("ButtonStyle4");
break;
case Key.D5:
case Key.NumPad5:
FiveBtn.Style = (Style) FindResource("ButtonStyle4");
break;
case Key.D6:
case Key.NumPad6:
SixBtn.Style = (Style) FindResource("ButtonStyle4");
break;
case Key.D7:
case Key.NumPad7:
SevenBtn.Style = (Style) FindResource("ButtonStyle4");
break;
case Key.D8:
case Key.NumPad8:
EightBtn.Style = (Style) FindResource("ButtonStyle4");
StarBtn.Style = (Style) FindResource("ButtonStyle4");
break;
case Key.D9:
case Key.NumPad9:
NineBtn.Style = (Style) FindResource("ButtonStyle4");
break;
default:
break;
}
}

Detect if any key is pressed in C# (not A, B, but any)

[DllImport("user32.dll", EntryPoint = "GetKeyboardState", SetLastError = true)]
private static extern bool NativeGetKeyboardState([Out] byte[] keyStates);

private static bool GetKeyboardState(byte[] keyStates)
{
if (keyStates == null)
throw new ArgumentNullException("keyState");
if (keyStates.Length != 256)
throw new ArgumentException("The buffer must be 256 bytes long.", "keyState");
return NativeGetKeyboardState(keyStates);
}

private static byte[] GetKeyboardState()
{
byte[] keyStates = new byte[256];
if (!GetKeyboardState(keyStates))
throw new Win32Exception(Marshal.GetLastWin32Error());
return keyStates;
}

private static bool AnyKeyPressed()
{
byte[] keyState = GetKeyboardState();
// skip the mouse buttons
return keyState.Skip(8).Any(state => (state & 0x80) != 0);
}

Capture key press in whole application

You could use something like this gist to register a global hook. It will fire whenever the given keys are pressed while your application is running. You can use it in your App class like this:

public partial class App
{
private HotKey _hotKey;

protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
RegisterHotKeys();
}

protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
UnregisterHotKeys();
}

private void RegisterHotKeys()
{
if (_hotKey != null) return;

_hotKey = new HotKey(ModifierKeys.Control | ModifierKeys.Shift, Key.V, Current.MainWindow);
_hotKey.HotKeyPressed += OnHotKeyPressed;
}

private void UnregisterHotKeys()
{
if (_hotKey == null) return;

_hotKey.HotKeyPressed -= OnHotKeyPressed;
_hotKey.Dispose();
}

private void OnHotKeyPressed(HotKey hotKey)
{
// Do whatever you want to do here
}
}


Related Topics



Leave a reply



Submit