diff --git a/README.md b/README.md
index 052d050..c8e2e87 100644
--- a/README.md
+++ b/README.md
@@ -50,3 +50,9 @@ The sample below shows some of the properties of the control. For a more compreh
```
+## Contributors and Thanks
+
+Hi, I'm Philipp! This little library was originally written by me, but is currently mostly maintained by [Jan Karger](https://github.com/punker76) and [Robin Krom](https://github.com/Lakritzator). Big, big kudos to the two of you, and everybody else who [contributed](https://github.com/hardcodet/wpf-notifyicon/graphs/contributors) to this library. You rock!
+
+Make sure to check out Robin's great [Greenshot](https://getgreenshot.org/) tool (that I use on a daily basis), and Jan's [MahApps](https://github.com/MahApps) UI framework.
+
diff --git a/src/NotifyIconWpf/Interop/KeyboardEvent.cs b/src/NotifyIconWpf/Interop/KeyboardEvent.cs
new file mode 100644
index 0000000..52b43ed
--- /dev/null
+++ b/src/NotifyIconWpf/Interop/KeyboardEvent.cs
@@ -0,0 +1,26 @@
+namespace Hardcodet.Wpf.TaskbarNotification.Interop
+{
+ ///
+ /// Event flags for keyboard events.
+ ///
+ public enum KeyboardEvent
+ {
+ ///
+ /// The icon was selected with the keyboard
+ /// and Shift-F10 was pressed.
+ ///
+ ContextMenu,
+
+ ///
+ /// The icon was selected with the keyboard
+ /// and activated with Spacebar or Enter.
+ ///
+ KeySelect,
+
+ ///
+ /// The icon was selected with the mouse
+ /// and activated with Enter.
+ ///
+ Select,
+ }
+}
diff --git a/src/NotifyIconWpf/Interop/WindowMessageSink.cs b/src/NotifyIconWpf/Interop/WindowMessageSink.cs
index 2ad3882..fafff14 100644
--- a/src/NotifyIconWpf/Interop/WindowMessageSink.cs
+++ b/src/NotifyIconWpf/Interop/WindowMessageSink.cs
@@ -94,6 +94,12 @@ public class WindowMessageSink : IDisposable
///
public event Action MouseEventReceived;
+ ///
+ /// Fired in case the user interacted with the taskbar
+ /// icon area with keyboard shortcuts.
+ ///
+ public event Action KeyboardEventReceived;
+
///
/// Fired if a balloon ToolTip was either displayed
/// or closed (indicated by the boolean flag).
@@ -242,8 +248,7 @@ private void ProcessWindowMessage(uint msg, IntPtr wParam, IntPtr lParam)
switch (message)
{
case WindowsMessages.WM_CONTEXTMENU:
- // TODO: Handle WM_CONTEXTMENU, see https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyiconw
- Debug.WriteLine("Unhandled WM_CONTEXTMENU");
+ KeyboardEventReceived?.Invoke(KeyboardEvent.ContextMenu);
break;
case WindowsMessages.WM_MOUSEMOVE:
@@ -313,13 +318,11 @@ private void ProcessWindowMessage(uint msg, IntPtr wParam, IntPtr lParam)
break;
case WindowsMessages.NIN_SELECT:
- // TODO: Handle NIN_SELECT see https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyiconw
- Debug.WriteLine("Unhandled NIN_SELECT");
+ KeyboardEventReceived?.Invoke(KeyboardEvent.Select);
break;
case WindowsMessages.NIN_KEYSELECT:
- // TODO: Handle NIN_KEYSELECT see https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyiconw
- Debug.WriteLine("Unhandled NIN_KEYSELECT");
+ KeyboardEventReceived?.Invoke(KeyboardEvent.KeySelect);
break;
default:
@@ -387,4 +390,4 @@ private void Dispose(bool disposing)
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/src/NotifyIconWpf/TaskbarIcon.Declarations.cs b/src/NotifyIconWpf/TaskbarIcon.Declarations.cs
index 2e994f3..1388864 100644
--- a/src/NotifyIconWpf/TaskbarIcon.Declarations.cs
+++ b/src/NotifyIconWpf/TaskbarIcon.Declarations.cs
@@ -1186,6 +1186,126 @@ internal static RoutedEventArgs RaiseTrayMouseMoveEvent(DependencyObject target)
#endregion
+ #region TrayKeyboardContextMenu
+
+ ///
+ /// TrayKeyboardContextMenu Routed Event
+ ///
+ public static readonly RoutedEvent TrayKeyboardContextMenuEvent = EventManager.RegisterRoutedEvent("TrayKeyboardContextMenu",
+ RoutingStrategy.Bubble, typeof (RoutedEventHandler), typeof (TaskbarIcon));
+
+ ///
+ /// Occurs when the user moves the mouse over the taskbar icon.
+ ///
+ public event RoutedEventHandler TrayKeyboardContextMenu
+ {
+ add { AddHandler(TrayKeyboardContextMenuEvent, value); }
+ remove { RemoveHandler(TrayKeyboardContextMenuEvent, value); }
+ }
+
+ ///
+ /// A helper method to raise the TrayKeyboardContextMenu event.
+ ///
+ protected RoutedEventArgs RaiseTrayKeyboardContextMenuEvent()
+ {
+ return RaiseTrayKeyboardContextMenuEvent(this);
+ }
+
+ ///
+ /// A static helper method to raise the TrayKeyboardContextMenu event on a target element.
+ ///
+ /// UIElement or ContentElement on which to raise the event
+ internal static RoutedEventArgs RaiseTrayKeyboardContextMenuEvent(DependencyObject target)
+ {
+ if (target == null) return null;
+
+ RoutedEventArgs args = new RoutedEventArgs(TrayKeyboardContextMenuEvent);
+ RoutedEventHelper.RaiseEvent(target, args);
+ return args;
+ }
+
+ #endregion
+
+ #region TrayKeyboardKeySelect
+
+ ///
+ /// TrayKeyboardKeySelect Routed Event
+ ///
+ public static readonly RoutedEvent TrayKeyboardKeySelectEvent = EventManager.RegisterRoutedEvent("TrayKeyboardKeySelect",
+ RoutingStrategy.Bubble, typeof (RoutedEventHandler), typeof (TaskbarIcon));
+
+ ///
+ /// Occurs when the user moves the mouse over the taskbar icon.
+ ///
+ public event RoutedEventHandler TrayKeyboardKeySelect
+ {
+ add { AddHandler(TrayKeyboardKeySelectEvent, value); }
+ remove { RemoveHandler(TrayKeyboardKeySelectEvent, value); }
+ }
+
+ ///
+ /// A helper method to raise the TrayKeyboardKeySelect event.
+ ///
+ protected RoutedEventArgs RaiseTrayKeyboardKeySelectEvent()
+ {
+ return RaiseTrayKeyboardKeySelectEvent(this);
+ }
+
+ ///
+ /// A static helper method to raise the TrayKeyboardKeySelect event on a target element.
+ ///
+ /// UIElement or ContentElement on which to raise the event
+ internal static RoutedEventArgs RaiseTrayKeyboardKeySelectEvent(DependencyObject target)
+ {
+ if (target == null) return null;
+
+ RoutedEventArgs args = new RoutedEventArgs(TrayKeyboardKeySelectEvent);
+ RoutedEventHelper.RaiseEvent(target, args);
+ return args;
+ }
+
+ #endregion
+
+ #region TrayKeyboardSelect
+
+ ///
+ /// TrayKeyboardSelect Routed Event
+ ///
+ public static readonly RoutedEvent TrayKeyboardSelectEvent = EventManager.RegisterRoutedEvent("TrayKeyboardSelect",
+ RoutingStrategy.Bubble, typeof (RoutedEventHandler), typeof (TaskbarIcon));
+
+ ///
+ /// Occurs when the user moves the mouse over the taskbar icon.
+ ///
+ public event RoutedEventHandler TrayKeyboardSelect
+ {
+ add { AddHandler(TrayKeyboardSelectEvent, value); }
+ remove { RemoveHandler(TrayKeyboardSelectEvent, value); }
+ }
+
+ ///
+ /// A helper method to raise the TrayKeyboardSelect event.
+ ///
+ protected RoutedEventArgs RaiseTrayKeyboardSelectEvent()
+ {
+ return RaiseTrayKeyboardSelectEvent(this);
+ }
+
+ ///
+ /// A static helper method to raise the TrayKeyboardSelect event on a target element.
+ ///
+ /// UIElement or ContentElement on which to raise the event
+ internal static RoutedEventArgs RaiseTrayKeyboardSelectEvent(DependencyObject target)
+ {
+ if (target == null) return null;
+
+ RoutedEventArgs args = new RoutedEventArgs(TrayKeyboardSelectEvent);
+ RoutedEventHelper.RaiseEvent(target, args);
+ return args;
+ }
+
+ #endregion
+
#region TrayBalloonTipShown
///
@@ -1893,4 +2013,4 @@ static TaskbarIcon()
ContextMenuProperty.OverrideMetadata(typeof (TaskbarIcon), md);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/NotifyIconWpf/TaskbarIcon.cs b/src/NotifyIconWpf/TaskbarIcon.cs
index 33bf8e3..aedf67c 100644
--- a/src/NotifyIconWpf/TaskbarIcon.cs
+++ b/src/NotifyIconWpf/TaskbarIcon.cs
@@ -132,6 +132,7 @@ public TaskbarIcon()
// register event listeners
messageSink.MouseEventReceived += OnMouseEvent;
+ messageSink.KeyboardEventReceived += OnKeyboardEvent;
messageSink.TaskbarCreated += OnTaskbarCreated;
messageSink.ChangeToolTipStateRequest += OnToolTipChange;
messageSink.BalloonToolTipChanged += OnBalloonToolTipChanged;
@@ -481,6 +482,37 @@ private void OnMouseEvent(MouseEvent me)
#endregion
+ #region Process Incoming Keyboard Events
+
+ ///
+ /// Processes keyboard events, which are bubbled
+ /// through the class' routed events, trigger
+ /// certain actions (e.g. show a popup), or
+ /// both.
+ ///
+ /// Event flag.
+ private void OnKeyboardEvent(KeyboardEvent ke)
+ {
+ if (IsDisposed) return;
+
+ switch (ke)
+ {
+ case KeyboardEvent.ContextMenu:
+ RaiseTrayKeyboardContextMenuEvent();
+ break;
+ case KeyboardEvent.KeySelect:
+ RaiseTrayKeyboardKeySelectEvent();
+ break;
+ case KeyboardEvent.Select:
+ RaiseTrayKeyboardSelectEvent();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(ke), "Missing handler for keyboard event flag: " + ke);
+ }
+ }
+
+ #endregion
+
#region ToolTips
///
@@ -762,6 +794,8 @@ private void ShowContextMenu(Point cursorPosition)
// fallback, the context menu can't receive keyboard events - should not happen though
WinApi.SetForegroundWindow(handle);
+ ContextMenu.Focus();
+
// bubble event
RaiseTrayContextMenuOpenEvent();
}
@@ -1100,4 +1134,4 @@ private void Dispose(bool disposing)
#endregion
}
-}
\ No newline at end of file
+}