Library that allows use Tray Icon in WPF Application. Ported from Windows Forms. Has wrapper for WPF ContexMenu (which converts it to Windows Forms ContextMenu). This is needed for good performance, and compatibility. This library targets all MVVM requirements:
- it has bindable properties
- it has interface with notify methods
Use one of the follwing methods to install and use this library:
Package Manager:
PM> Install-Package TrayIcon.NET CLI:
> dotnet add package TrayIcon
First you need to include namespace to your code or markup.
For XAML it can look like:
<Windowxmlns:icon="https://github.com/nullsoftware/TrayIcon" />And for C#:
usingNullSoftware.ToolKit;Then you can place tray icon inside your window, or keep it in variable/property.
For XAML:
<icon:TrayIconHandlers.TrayIcons>
<icon:TrayIconTitle="My Application"IconSource="MainIcon.ico"ClickCommand="{Binding ExampleCommand}"NotificationServiceMemberPath="NotificationService"/>
</icon:TrayIconHandlers.TrayIcons>For C#:
TrayIconmyTrayIcon=newTrayIcon(){Title="My Application",IconSource=newBitmapImage(newUri("pack://application:,,,/MainIcon.ico")),ClickCommand=newRelayCommand(ExampleAction)};To show balloon you need to call Notify method:
INotificationServicenotifyService=myTrayIcon;notifyService.Notify("Greetings","Hello World!",NotificationType.Information);Note:INotificationService can be obtained from XAML by using NotificationServiceMemberPath.
It injects INotificationService to specified DataContext property.
To support latest .NET versions was added ContextMenuVariation property to TrayIcon.
It allows to switch beetwen System.Windows.Forms.ContextMenu or System.Windows.Forms.ContextMenuStrip.
Warning: .NET Core 3.1 and later versions don't support System.Windows.Forms.ContextMenu.
See more at the documentation page.
Any MenuItem (or the root ContextMenu) can have its children built from an ObservableCollection<MenuItem> bound through ItemsSource. Adds, removes, replaces, moves and Clear() (Reset) are forwarded to the underlying tray menu — no need to rebuild the menu manually.
XAML:
<MenuItemHeader="Recent Files"ItemsSource="{Binding RecentFiles}"/>ViewModel:
publicObservableCollection<MenuItem>RecentFiles{get;}=newObservableCollection<MenuItem>();privatevoidRegisterRecent(stringpath){RecentFiles.Add(newMenuItem{Header=path,Command=newRelayCommand(()=>Process.Start(path)),});}The items in the bound collection must be System.Windows.Controls.MenuItem (or Separator) instances. Direct mutation of MenuItem.Items from code-behind is also supported.
WPF MenuItem.Icon is forwarded to ToolStripMenuItem.Image automatically. Both static and bound icons are supported, and changes update live.
<MenuItemHeader="Open">
<MenuItem.Icon>
<ImageSource="open.png"/>
</MenuItem.Icon>
</MenuItem>Accepted Icon values: a BitmapSource, an Image element with any ImageSource (the typical XAML form above), or any other ImageSource such as DrawingImage — non-bitmap sources are rendered to a 16×16 PNG.
Warning: icons are only rendered when ContextMenuVariation is ContextMenuStrip (the default on .NET Core 3.0+). The legacy System.Windows.Forms.MenuItem used by ContextMenuVariation.ContextMenu has no icon property.
<Windowx:Class="TrayIcon.Example.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:TrayIcon.Example"xmlns:vm="clr-namespace:TrayIcon.Example.ViewModels"xmlns:icon="https://github.com/nullsoftware/TrayIcon"mc:Ignorable="d"Title="MainWindow"Height="450"Width="800"Icon="MainIcon.ico">
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<!--Here you can place your icons-->
<icon:TrayIconHandlers.TrayIcons>
<icon:TrayIconTitle="My Application"IconSource="MainIcon.ico"DoubleClickCommand="{Binding MinimazeCommand}"NotificationServiceMemberPath="NotificationService">
<icon:TrayIcon.ContextMenu>
<!--This context menu will be converted to System.Windows.Forms.ContextMenu-->
<ContextMenu>
<MenuItemHeader="Notify"Command="{Binding SayHelloCommand}" icon:TrayIcon.IsDefault="True"/>
<Separator/>
<MenuItemHeader="_Silent Mode"IsCheckable="True"IsChecked="{Binding IsSilentModeEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Separator/>
<MenuItemHeader="E_xit"Command="{Binding CloseCommand}"/>
</ContextMenu>
</icon:TrayIcon.ContextMenu>
</icon:TrayIcon>
</icon:TrayIconHandlers.TrayIcons>
<Grid>
<StackPanelVerticalAlignment="Top"HorizontalAlignment="Left"Margin="10, 20"Orientation="Horizontal">
<CheckBoxVerticalAlignment="Center"IsChecked="{Binding IsSilentModeEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"Content="Silent Mode"/>
<ButtonMargin="20, 0, 0, 0"Padding="10, 3"MinWidth="86"Command="{Binding SayHelloCommand}"Content="Notify"/>
</StackPanel>
</Grid>
</Window>ViewModel:
usingSystem;usingSystem.Windows.Input;usingNullSoftware.ToolKit;usingPropertyChanged;publicclassMainViewModel:ObservableObject{publicboolIsSilentModeEnabled{get;set;}[DoNotNotify]publicICommandMinimazeCommand{get;}[DoNotNotify]publicICommandSayHelloCommand{get;}[DoNotNotify]publicICommandCloseCommand{get;}[DoNotNotify]privateINotificationServiceNotificationService{get;set;}publicMainViewModel(){MinimazeCommand=newRelayCommand(()=>App.Current.MainWindow.WindowState=System.Windows.WindowState.Minimized);SayHelloCommand=newRelayCommand(()=>NotificationService.Notify("Greetings","Hello World!"));CloseCommand=newRelayCommand(App.Current.MainWindow.Close);}}Note: in ViewModel was used PropertyChanged.Fody plugin, to simplify usage of INotifyPropertyChanged.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Developed by Null Software