-
Notifications
You must be signed in to change notification settings - Fork 25
Tray Service
The tray service provides notification area icon support.
There are a few objects involved with using the tray service:
-
NotificationArea
provides thePinnedIcons
andUnpinnedIcons
collections, as well as service management and lifecycle methods. -
TrayService
provides the Windows hook functionality allowing applications to display notification icons usingShell_NotifyIcon
. -
ExplorerTrayService
provides icons from the Windows Explorer notification area as a seed. -
NotifyIcon
objects represent each individual notification icon.
If you are using the ShellManager
to instantiate ManagedShell, the lifecycle of the tray service is managed for you. However, you can customize this behavior by passing a ShellConfig
object to the ShellManager
constructor to modify settings:
// Initialize the default configuration.
ShellConfig config = ShellManager.DefaultShellConfig;
// Customize tray service options.
config.EnableTrayService = true; // controls whether the tray objects are instantiated in ShellManager, true by default
config.AutoStartTrayService = false; // controls whether the tray service is started as part of ShellManager instantiation, true by default
config.PinnedNotifyIcons = new[] { "GUID or PathToExe:UID" }; // sets the initial NotifyIcons that should be included in the PinnedIcons collection, by default Action Center (prior to Windows 10 only), Power, Network, and Volume.
// Initialize the shell manager.
ShellManager _shellManager = new ShellManager(config);
// Initialize the tray service, since we disabled auto-start above.
_shellManager.NotificationArea.Initialize();
The tray service can be used without management via ShellManager
if desired.
using ManagedShell.WindowsTray;
...
// Instantiate service.
TrayService trayService = new TrayService();
ExplorerTrayService explorerTrayService = new ExplorerTrayService();
// Initialize collections with the default set of pinned icons.
NotificationArea notificationArea = new NotificationArea(NotificationArea.DEFAULT_PINNED, trayService, explorerTrayService);
// Start the service.
notificationArea.Initialize();
// Access the notification area icons.
MyTrayPinnedItemsControl.ItemsSource = notificationArea.PinnedIcons;
MyTrayUnpinnedItemsControl.ItemsSource = notificationArea.UnpinnedIcons;
// Dispose of the service.
notificationArea.Dispose();
Pinned icons allow certain notification icons to be displayed separately in your UI. By default, if you use ShellManager
, the Action Center (prior to Windows 10 only), Power, Network, and Volume icons will be included in this collection (the default pinned icons are defined in NotificationArea.DEFAULT_PINNED
).
To allow for your application to manage pinned icons, several methods are available on each NotifyIcon
object in the PinnedIcons
and UnpinnedIcons
collections:
private void DoStuffToNotifyIcon(NotifyIcon icon)
{
// Mark icon as pinned
icon.Pin();
// Mark icon as pinned, at a specific index in the collection
icon.Pin(3);
// Mark icon as unpinned
icon.Unpin();
}
The NotificationArea
object also can also be used to update pinned icons post-instantiation:
// Replace the pinned icons string array with a new one.
notificationArea.SetPinnedIcons(myPinnedIcons);
Many notification area icons request for the size and placement of the notification area control, usually for the purpose of positioning a "flyout" interface style. Windows Explorer uses the placement of the taskbar for this purpose, but the control to return placement data from will vary by application.
To set the tray host size, create a TrayHostSizeData
struct and pass it to the NotificationArea.SetTrayHostSizeData
method:
private void SetTrayHostSizeData()
{
// Create struct with size and position data about the tray host control
TrayHostSizeData hostSize = new TrayHostSizeData
{
edge = NativeMethods.ABEdge.ABE_BOTTOM,
rc = new NativeMethods.Rect
{
Top = 0,
Left = 0,
Bottom = 0,
Right = 0
}
};
// Set the tray host size data.
notificationArea.SetTrayHostSizeData(hostSize);
}
If you are using the AppBarWindow
class, you can override the AfterAppBarPos
and SetPosition
methods to automatically set this whenever the window's position or size change:
private void SetTrayHostSizeData()
{
// Create struct with size and position data about the tray host control
TrayHostSizeData hostSize = new TrayHostSizeData
{
edge = AppBarEdge,
rc = new NativeMethods.Rect
{
Top = (int)(Top * DpiScale),
Left = (int)(Left * DpiScale),
Bottom = (int)((Top + Height) * DpiScale),
Right = (int)((Left + Width) * DpiScale)
}
};
// Set the tray host size data.
notificationArea.SetTrayHostSizeData(hostSize);
}
public override void AfterAppBarPos(bool isSameCoords, NativeMethods.Rect rect)
{
base.AfterAppBarPos(isSameCoords, rect);
SetTrayHostSizeData();
}
public override void SetPosition()
{
base.SetPosition();
SetTrayHostSizeData();
}
User interaction with notification area icons can be achieved by calling the appropriate methods on the NotifyIcon
object from your notification area icon control.
Several mouse events should call corresponding methods on the NotifyIcon
object, passing in the cursor position:
private void NotifyIcon_OnMouseUp(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
notifyIcon.IconMouseClick(e.ChangedButton, MouseHelper.GetCursorPositionParam(), System.Windows.Forms.SystemInformation.DoubleClickTime);
}
private void NotifyIcon_OnMouseLeave(object sender, MouseEventArgs e)
{
e.Handled = true;
notifyIcon.IconMouseLeave(MouseHelper.GetCursorPositionParam());
}
private void NotifyIcon_OnMouseMove(object sender, MouseEventArgs e)
{
e.Handled = true;
notifyIcon.IconMouseMove(MouseHelper.GetCursorPositionParam());
}
The MouseEnter
event should additionally set the NotifyIcon.Placement
property based on the notification area icon control's placement. Several notification area icons request the placement of the notification area icon control, and setting this on the MouseEnter
event allows this information to be as accurate as possible once the icon is interacted with.
// In this example, mouse events are being sent from a WPF Border, which contains an Image.
// This allows for any margin around the icon image to respond to mouse events.
// As such, we make the assumption that the sender is a Decorator.
private void NotifyIcon_OnMouseEnter(object sender, MouseEventArgs e)
{
e.Handled = true;
// get icon position for Shell_NotifyIconGetRect
Decorator sendingDecorator = sender as Decorator;
Point location = sendingDecorator.PointToScreen(new Point(0, 0));
double dpiScale = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice.M11;
// set icon position for Shell_NotifyIconGetRect
notifyIcon.Placement = new NativeMethods.Rect
{
Top = (int)location.Y,
Left = (int)location.X,
Bottom = (int)(sendingDecorator.ActualHeight * dpiScale),
Right = (int)(sendingDecorator.ActualWidth * dpiScale)
};
notifyIcon.IconMouseEnter(MouseHelper.GetCursorPositionParam());
}