diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj
index 95ca4e3e9..340e32459 100644
--- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj
+++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj
@@ -91,6 +91,7 @@
+
diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj
index 688fcadc2..3ed70b8f7 100644
--- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj
+++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj
@@ -91,6 +91,7 @@
+
diff --git a/Fluent.Ribbon/Internal/FocusWrapper.cs b/Fluent.Ribbon/Internal/FocusWrapper.cs
new file mode 100644
index 000000000..3538390e6
--- /dev/null
+++ b/Fluent.Ribbon/Internal/FocusWrapper.cs
@@ -0,0 +1,54 @@
+namespace Fluent.Internal
+{
+ using System;
+ using System.Windows;
+ using System.Windows.Input;
+
+ internal class FocusWrapper
+ {
+ private readonly IInputElement inputElement;
+ private readonly IntPtr handle;
+
+ private FocusWrapper(IInputElement inputElement)
+ {
+ this.inputElement = inputElement;
+ }
+
+ private FocusWrapper(IntPtr handle)
+ {
+ this.handle = handle;
+ }
+
+ public void Focus()
+ {
+ if (this.inputElement != null)
+ {
+ this.inputElement.Focus();
+ return;
+ }
+
+ if (this.handle != IntPtr.Zero)
+ {
+ NativeMethods.SetFocus(this.handle);
+ return;
+ }
+ }
+
+ public static FocusWrapper GetWrapperForCurrentFocus()
+ {
+ if (Keyboard.FocusedElement != null)
+ {
+ return new FocusWrapper(Keyboard.FocusedElement);
+ }
+
+ var handle = NativeMethods.GetFocus();
+
+ if (handle != IntPtr.Zero)
+ {
+ return new FocusWrapper(handle);
+ }
+
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Fluent.Ribbon/Internal/NativeMethods.cs b/Fluent.Ribbon/Internal/NativeMethods.cs
index 4d9236a61..0187f8485 100644
--- a/Fluent.Ribbon/Internal/NativeMethods.cs
+++ b/Fluent.Ribbon/Internal/NativeMethods.cs
@@ -207,6 +207,12 @@ internal static void PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lP
[DllImport("user32.dll")]
internal static extern int SetWindowLong(IntPtr hWnd, GWL nIndex, WS dwNewLong);
+ [DllImport("user32.dll")]
+ internal static extern IntPtr GetFocus();
+
+ [DllImport("user32.dll")]
+ internal static extern IntPtr SetFocus(IntPtr hWnd);
+
/// Add and remove a native WindowStyle from the HWND.
/// A HWND for a window.
/// The styles to be removed. These can be bitwise combined.
diff --git a/Fluent.Ribbon/Services/KeyTipService.cs b/Fluent.Ribbon/Services/KeyTipService.cs
index 0eaf26638..08ebb5bf4 100644
--- a/Fluent.Ribbon/Services/KeyTipService.cs
+++ b/Fluent.Ribbon/Services/KeyTipService.cs
@@ -25,7 +25,8 @@ internal class KeyTipService
// Is KeyTips Actived now
private KeyTipAdorner activeAdornerChain;
// This element must be remembered to restore it
- IInputElement backUpFocusedElement;
+ FocusWrapper backUpFocusedControl;
+
// Window where we attached
private Window window;
@@ -117,10 +118,7 @@ public void Attach()
// Hookup non client area messages
this.attachedHwndSource = (HwndSource)PresentationSource.FromVisual(this.window);
- if (this.attachedHwndSource != null)
- {
- this.attachedHwndSource.AddHook(this.WindowProc);
- }
+ this.attachedHwndSource?.AddHook(this.WindowProc);
}
///
@@ -146,12 +144,8 @@ public void Detach()
this.window = null;
}
- // Hookup non client area messages
- if (this.attachedHwndSource != null
- && this.attachedHwndSource.IsDisposed == false)
- {
- this.attachedHwndSource.RemoveHook(this.WindowProc);
- }
+ // Unhook non client area messages
+ this.attachedHwndSource?.RemoveHook(this.WindowProc);
}
// Window's messages hook up
@@ -210,11 +204,6 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e)
else if (this.activeAdornerChain != null
&& this.activeAdornerChain.IsAdornerChainAlive)
{
- // Focus ribbon
- this.backUpFocusedElement = Keyboard.FocusedElement;
- this.ribbon.Focusable = true;
- //this.ribbon.Focus();
-
this.activeAdornerChain.Terminate();
}
else
@@ -228,6 +217,7 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e)
this.activeAdornerChain.ActiveKeyTipAdorner.Back();
this.ClearUserInput();
e.Handled = true;
+ return;
}
else
{
@@ -238,6 +228,15 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e)
return;
}
+ 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));
+
+ // Don't do anything and let WPF handle the rest
+ if (isKeyRealInput == false)
+ {
+ return;
+ }
+
// Should we show the keytips and immediately react to key?
if (this.activeAdornerChain == null
|| this.activeAdornerChain.IsAdornerChainAlive == false
@@ -251,25 +250,28 @@ private void OnWindowPreviewKeyDown(object sender, KeyEventArgs e)
return;
}
- string previousInput = this.currentUserInput;
-
- this.currentUserInput += keyConverter.ConvertToString(e.Key == Key.System ? e.SystemKey : e.Key);
+ var previousInput = this.currentUserInput;
+ this.currentUserInput += keyConverter.ConvertToString(actualKey);
// 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)
{
this.currentUserInput = previousInput;
System.Media.SystemSounds.Beep.Play();
+ e.Handled = true;
+ return;
}
else if (this.activeAdornerChain.ActiveKeyTipAdorner.Forward(this.currentUserInput, true))
{
this.ClearUserInput();
e.Handled = true;
+ return;
}
else
{
this.activeAdornerChain.ActiveKeyTipAdorner.FilterKeyTips(this.currentUserInput);
e.Handled = true;
+ return;
}
}
}
@@ -315,13 +317,8 @@ private void ClearUserInput()
private void RestoreFocus()
{
- if (this.backUpFocusedElement != null)
- {
- this.backUpFocusedElement.Focus();
- this.backUpFocusedElement = null; // Release the reference, so GC can work
- }
-
- this.ribbon.Focusable = false;
+ this.backUpFocusedControl?.Focus();
+ this.backUpFocusedControl = null;
}
private void OnAdornerChainTerminated(object sender, EventArgs e)
@@ -343,28 +340,20 @@ private void OnDelayedShow(object sender, EventArgs e)
private void ShowImmediatly()
{
- this.timer.Stop();
- this.backUpFocusedElement = Keyboard.FocusedElement;
-
- // Focus ribbon
- this.ribbon.Focusable = true;
- //this.ribbon.Focus();
-
this.Show();
}
private void ShowDelayed()
{
- if (this.activeAdornerChain != null)
- {
- this.activeAdornerChain.Terminate();
- }
+ this.activeAdornerChain?.Terminate();
this.timer.Start();
}
private void Show()
{
+ this.timer.Stop();
+
// Check whether the window is
// - still present (prevents exceptions when window is closed by system commands)
// - still active (prevents keytips showing during Alt-Tab'ing)
@@ -375,6 +364,11 @@ private void Show()
return;
}
+ this.backUpFocusedControl = FocusWrapper.GetWrapperForCurrentFocus();
+
+ // Focus ribbon
+ this.ribbon.Focus();
+
this.ClearUserInput();
this.activeAdornerChain = new KeyTipAdorner(this.ribbon, this.ribbon, null);
@@ -435,9 +429,10 @@ private ApplicationMenu GetApplicationMenu()
private void DirectlyForwardToSpecialControl(DependencyObject specialControl)
{
var keys = KeyTip.GetKeys(specialControl);
+
if (string.IsNullOrEmpty(keys) == false)
{
- this.activeAdornerChain.Forward(KeyTip.GetKeys(specialControl), false);
+ this.activeAdornerChain.Forward(keys, false);
}
else
{