diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj
index efa71ee8c..ce29a67c1 100644
--- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj
+++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj
@@ -94,6 +94,7 @@
+
diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj
index 2749a737c..e69c5c5fa 100644
--- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj
+++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj
@@ -94,6 +94,7 @@
+
diff --git a/Fluent.Ribbon/Internal/KeyEventUtility.cs b/Fluent.Ribbon/Internal/KeyEventUtility.cs
new file mode 100644
index 000000000..e84e023c5
--- /dev/null
+++ b/Fluent.Ribbon/Internal/KeyEventUtility.cs
@@ -0,0 +1,34 @@
+namespace Fluent.Internal
+{
+ using System.Windows.Input;
+
+ internal static class KeyEventUtility
+ {
+ public static string GetStringFromKey(Key key)
+ {
+ var keyboardState = new byte[256];
+ if (NativeMethods.GetKeyboardState(keyboardState) == false)
+ {
+ return null;
+ }
+
+ var virtualKey = KeyInterop.VirtualKeyFromKey(key);
+ var scanCode = NativeMethods.MapVirtualKey((uint)virtualKey, NativeMethods.MapType.MAPVK_VK_TO_VSC);
+ var chars = new char[1];
+
+ var result = NativeMethods.ToUnicode((uint)virtualKey, scanCode, keyboardState, chars, chars.Length, 0);
+ switch (result)
+ {
+ case -1:
+ case 0:
+ return null;
+
+ case 1:
+ return chars[0].ToString();
+
+ default:
+ return null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Fluent.Ribbon/Internal/NativeMethods.cs b/Fluent.Ribbon/Internal/NativeMethods.cs
index 0187f8485..b99c006f8 100644
--- a/Fluent.Ribbon/Internal/NativeMethods.cs
+++ b/Fluent.Ribbon/Internal/NativeMethods.cs
@@ -236,6 +236,25 @@ public static bool _ModifyStyle(this IntPtr _hwnd, WS removeStyle, WS addStyle)
return true;
}
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ internal static extern int ToUnicode(uint virtualKey, uint scanCode, byte[] keyStates, [MarshalAs(UnmanagedType.LPArray)] [Out] char[] chars, int charMaxCount, uint flags);
+
+ [DllImport("user32.dll")]
+ internal static extern bool GetKeyboardState(byte[] lpKeyState);
+
+ [DllImport("user32.dll")]
+ internal static extern uint MapVirtualKey(uint uCode, MapType uMapType);
+
+ // ReSharper disable InconsistentNaming
+ internal enum MapType : uint
+ {
+ MAPVK_VK_TO_VSC = 0x0,
+ MAPVK_VSC_TO_VK = 0x1,
+ MAPVK_VK_TO_CHAR = 0x2,
+ MAPVK_VSC_TO_VK_EX = 0x3,
+ }
+ // ReSharper restore InconsistentNaming
+
///
/// GetWindowLong values, GWL_*
///
diff --git a/Fluent.Ribbon/Services/KeyTipService.cs b/Fluent.Ribbon/Services/KeyTipService.cs
index d175c8b06..16005528e 100644
--- a/Fluent.Ribbon/Services/KeyTipService.cs
+++ b/Fluent.Ribbon/Services/KeyTipService.cs
@@ -36,7 +36,6 @@ internal class KeyTipService
// Attached HWND source
private HwndSource attachedHwndSource;
- private static readonly KeyConverter keyConverter = new KeyConverter();
private string currentUserInput;
///
@@ -171,13 +170,15 @@ private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, re
private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e)
{
- if (e.IsRepeat)
+ if (e.IsRepeat
+ || e.Handled)
{
return;
}
if (this.ribbon.IsCollapsed
- || this.ribbon.IsEnabled == false)
+ || this.ribbon.IsEnabled == false
+ || this.window.IsActive == false)
{
return;
}
@@ -224,7 +225,9 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e)
}
var actualKey = e.Key == Key.System ? e.SystemKey : e.Key;
- var isKeyRealInput = ((actualKey >= Key.A && actualKey <= Key.Z) || (actualKey >= Key.D0 && actualKey <= Key.D9) || (actualKey >= Key.NumPad0 && actualKey <= Key.NumPad9));
+ // we need to get the real string input for the key because of keys like ä,ö,ü #258
+ var key = KeyEventUtility.GetStringFromKey(actualKey);
+ var isKeyRealInput = string.IsNullOrEmpty(key) == false;
// Don't do anything and let WPF handle the rest
if (isKeyRealInput == false)
@@ -240,12 +243,15 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e)
return;
}
+ var shownImmediately = false;
+
// Should we show the keytips and immediately react to key?
if (this.activeAdornerChain == null
|| this.activeAdornerChain.IsAdornerChainAlive == false
|| this.activeAdornerChain.AreAnyKeyTipsVisible == false)
{
this.ShowImmediatly();
+ shownImmediately = true;
}
if (this.activeAdornerChain == null)
@@ -254,11 +260,18 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e)
}
var previousInput = this.currentUserInput;
- this.currentUserInput += keyConverter.ConvertToString(actualKey);
+ this.currentUserInput += key;
- // If no key tips match the current input, continue with the previously entered and still correct keys.
if (this.activeAdornerChain.ActiveKeyTipAdorner.ContainsKeyTipStartingWith(this.currentUserInput) == false)
{
+ // Handles access-keys #258
+ if (shownImmediately)
+ {
+ this.activeAdornerChain?.Terminate();
+ return;
+ }
+
+ // If no key tips match the current input, continue with the previously entered and still correct keys.
this.currentUserInput = previousInput;
System.Media.SystemSounds.Beep.Play();
e.Handled = true;
@@ -282,8 +295,10 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e)
private void OnWindowKeyUp(object sender, KeyEventArgs e)
{
if (this.ribbon.IsCollapsed
- || this.ribbon.IsEnabled == false)
+ || this.ribbon.IsEnabled == false
+ || this.window.IsActive == false)
{
+ this.activeAdornerChain?.Terminate();
return;
}