官网:https://www.codeproject.com/Articles/36468/WPF-NotifyIcon-2#wpfapi
NotifyIcon是帮助WPF应用实现系统托盘图标的一个开源库,可以在WPF中,以控件的方式来设置系统托盘图标的工具提示、弹出窗口、上下文菜单和气球消息等。
安装依赖库
注意,NotifyIcon可以以XAML或C#代码的方式去实现功能,但无论哪种方式来实现系统托盘图标,其存在时间都是从TaskbarIcon
对象的创建,到该对象的消亡或调用了Dispose()
方法。当然,作为控件对象,其本身具有Visibility
属性,可以通过Visibility
来隐藏或显示系统托盘图标。
做法很简单,只要引入命名空间xmlns:tb="http://www.hardcodet.net/taskbar"
,然后使用TaskbarIcon
控件就可以了。
这里是直接将窗口隐藏了,打开应用后就只有右下角的图标了。
<Window x:Class="NotifyIconTest.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:tb="http://www.hardcodet.net/taskbar"
xmlns:local="clr-namespace:NotifyIconTest"
mc:Ignorable="d" Visibility="Hidden"
Title="MainWindow" Height="450" Width="800">
<Grid>
<tb:TaskbarIcon IconSource="/Icons/bitbug_favicon.ico" ToolTipText="hello world"/>
</Grid>
</Window>
在代码中,只要创建了TaskbarIcon
对象,并进行了响应设置,系统托盘图标就会出现,这里的效果跟上面XAML的方式是一样的。
TaskbarIcon tbi = new TaskbarIcon();
tbi.Icon = new Icon("bitbug_favicon.ico");
tbi.ToolTipText = "hello world";
除了上面单独使用XAML或单独使用C#外,当然也可以将两者结合使用,这个就是WPF的基本功了,随便举个用例。
定义资源字典
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="http://www.hardcodet.net/taskbar">
<tb:TaskbarIcon x:Key="MyNotifyIcon" IconSource="/Icons/bitbug_favicon.ico" ToolTipText="hello world" />
ResourceDictionary>
全局引用
<Application ......>
<Application.Resources>
<ResourceDictionary Source="Icons.xaml"/>
Application.Resources>
Application>
代码获取对象
public partial class MainWindow : Window
{
private TaskbarIcon _tb;
public MainWindow()
{
InitializeComponent();
_tb = (TaskbarIcon)FindResource("MyNotifyIcon");
}
}
很多情况下,仅仅展示个系统托盘图标是不够的,需要给其添加如工具提示、弹出窗口、上下文菜单、气球提示等功能。
注意:只有当前没有显示其他控件(弹出窗口、上下文菜单或自定义气球提示)时,才会显示工具提示。
TaskbarIcon
类中提供了两个与工具提示相关的属性,分别是TrayToolTip
和ToolTipText
。
TrayToolTip
TrayToolTip
属性可以接收任意的UIElement
对象,当用户将鼠标悬停在系统托盘图标上时,该元素就会显示出来。可以是一个用户控件,一个按钮,一个图像控件或任何其他控件。
ToolTipText
ToolTipText
属性接收一个提示信息的字符串,在以下两种场景下,鼠标悬停在系统托盘图标上时会显式提示信息:
<Window ......
xmlns:tb="http://www.hardcodet.net/taskbar"
......>
<Grid>
<tb:TaskbarIcon IconSource="/ZoomOut.ico" ToolTipText="ToolTipText">
<tb:TaskbarIcon.TrayToolTip>
<Border Background="White" BorderBrush="Orange" BorderThickness="2" CornerRadius="4" Opacity="0.8" Width="160" Height="40">
<TextBlock Text="hello world" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</tb:TaskbarIcon.TrayToolTip>
</tb:TaskbarIcon>
</Grid>
</Window>
虽然可以以内联的方式完整的定义TrayTToolTip
,但当需求复杂,工具提示的内容较多时,更希望能够将工具提示的内容单独分离成用户控件来进行管理,这一点NotifyIcon也是支持的,只需要在xaml中,直接将对应的用户控件作为TrayToolTip
的元素即可。
用户控件的定义
<UserControl x:Class="NotifyIconTest.SimpleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:NotifyIconTest"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Border Background="White" BorderBrush="Orange" BorderThickness="2" CornerRadius="4" Opacity="0.8" Width="160" Height="40">
<TextBlock Text="hello world" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</UserControl>
用户控件的使用
<tb:TaskbarIcon IconSource="/Icons/bitbug_favicon.ico" ToolTipText="系统托盘图标测试">
<tb:TaskbarIcon.TrayToolTip>
<local:SimpleUserControl/>
</tb:TaskbarIcon.TrayToolTip>
</tb:TaskbarIcon>
除了上面两种方式外,还可以将TrayToolTip
的内容控件定义为资源,然后在TaskbarIcon
控件上以属性的方式来设置TrayToolTip
。
<Window ......
xmlns:tb="http://www.hardcodet.net/taskbar"
......>
<Window.Resources>
<Border x:Key="trayToolTip" Background="White" BorderBrush="Orange" BorderThickness="2" CornerRadius="4" Opacity="0.8" Width="160" Height="40">
<TextBlock Text="hello world" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</Window.Resources>
<Grid>
<tb:TaskbarIcon IconSource="/Icons/bitbug_favicon.ico" ToolTipText="系统托盘图标测试" TrayToolTip="{StaticResource trayToolTip}"/>
</Grid>
</Window>
与工具提示不同,默认情况下,弹框只有在鼠标点击系统托盘图标时才会展示,并且在鼠标左键点击其他任意地方时关闭。因此可以在弹框中加入一些应用的快捷方式等交互内容。
TaskbarIcon
类中提供了两个与弹框的相关的属性,分别为TrayPopup
和PopupActivation
。
TrayPopup
TrayPopup
属性可以接收任意的UIElement
对象,默认情况下,在鼠标左键点击系统托盘图标时该UIElement
对象就会展现,并且在鼠标左键点击其他任意地方时关闭。
PopupActivation
PopupActivation
属性用于设置TrayPopup
的触发条件,有效值如下:
LeftClick
(默认)RightClick
DoubleClick
LeftOrRightClick
LeftOrDoubleClick
MiddleClick
All
TrayPopup
的用法与TrayToolTip
的用法完全一样,这里就只做内联方式的演示,其他的可以参考上文TrayToolTip
的用法。
<Window ......
xmlns:tb="http://www.hardcodet.net/taskbar"
......>
<Grid>
<tb:TaskbarIcon IconSource="/Icons/bitbug_favicon.ico" ToolTipText="系统托盘图标测试" PopupActivation="LeftOrDoubleClick">
<tb:TaskbarIcon.TrayPopup>
<Border Background="White" BorderBrush="Orange" BorderThickness="2" CornerRadius="4" Width="160" Height="80">
<StackPanel>
<Button Content="开启" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 10" Width="100"/>
<Button Content="关闭" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 10" Width="100"/>
</StackPanel>
</Border>
</tb:TaskbarIcon.TrayPopup>
</tb:TaskbarIcon>
</Grid>
</Window>
默认情况下,当鼠标右键点击系统托盘图标时,可以显示标准的WPF右键菜单。
TaskbarIcon
类中提供了两个与菜单相关的属性,分别为ContextMenu
和MenuActivation
。
ContextMenu
ContextMenu
属性接收一个WPF的原生ContextMenu
控件作为展示内容,跟原生用法是一样的,没啥好说。
MenuActivation
MenuActivation
属性用于设置菜单的打开条件,有效值如下:
LeftClick
RightClick
(默认)DoubleClick
LeftOrRightClick
LeftOrDoubleClick
MiddleClick
All
注意,如果MenuActivation
和PopupActivation
定义了同样的值,菜单的优先级会高于弹框。
<Window ......
xmlns:tb="http://www.hardcodet.net/taskbar"
......>
<Grid>
<tb:TaskbarIcon IconSource="/Icons/bitbug_favicon.ico" ToolTipText="系统托盘图标测试">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu Background="LightCoral">
<MenuItem Header="First Menu Item" />
<MenuItem Header="Second Menu Item" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
</Grid>
</Window>
NotifyIcon支持两种气球提示,可以使用它们在托盘区域显示信息:
UIElements
变成气球消息。这不仅意味着我们可以根据自己的喜好设置气球的样式,而且由于NotifyIcon的丰富事件模型,我们还可以创建精美的动画。要展示标准气球,需要调用TaskbarIcon
对象的ShowBalloonTip()
方法。
ShowBalloonTip(string title, string message, BalloonIcon symbol)
:展示标准气球提示。
title
:标题。message
:信息。symbol
:标准图标。ShowBalloonTip(string title, string message, Icon customIcon, bool largeIcon = false)
:TaskbarIcon
的实例方法,展示标准气球提示,可以使用自定义图标。
customIcon
:自定义图标。largeIcon
:是否允许使用大型图标。xaml定义气球的触发事件
这里为了方便,随便搞个事件定义一下,实际情况肯定是根据项目来了
<Window ......
xmlns:tb="http://www.hardcodet.net/taskbar"
......>
<Grid>
<tb:TaskbarIcon x:Name="MyNotifyIcon" IconSource="/Icons/bitbug_favicon.ico" ToolTipText="系统托盘图标测试">
<tb:TaskbarIcon.TrayPopup>
<Border Background="White" BorderBrush="Orange" BorderThickness="2" CornerRadius="4" Width="160" Height="80">
<Button Content="气球" Click="Button_Click" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 10" Width="100"/>
Border>
tb:TaskbarIcon.TrayPopup>
tb:TaskbarIcon>
Grid>
Window>
事件的实现
private void Button_Click(object sender, RoutedEventArgs e)
{
MyNotifyIcon.ShowBalloonTip("气球提示", "气球提示内容", BalloonIcon.Warning);
}
要展示自定义气球,需要调用TaskbarIcon
对象的ShowCustomBalloon()
方法。
ShowCustomBalloon(UIElement balloon, PopupAnimation animation, int? timeout)
:TaskbarIcon
的实例方法,用于展示自定义气球。
balloon
:要展示的自定义元素。animation
:NotityIcon提供的预定义气球动画。timeout
:超时时间(毫秒,且最少要500毫秒),超过时间后关闭气球。xaml定义气球的触发事件
<Window ......
xmlns:tb="http://www.hardcodet.net/taskbar"
......>
<Grid>
<tb:TaskbarIcon x:Name="MyNotifyIcon" IconSource="/Icons/bitbug_favicon.ico" ToolTipText="系统托盘图标测试">
<tb:TaskbarIcon.TrayPopup>
<Border Background="White" BorderBrush="Orange" BorderThickness="2" CornerRadius="4" Width="160" Height="80">
<Button Content="气球" Click="Button_Click" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 10" Width="100"/>
Border>
tb:TaskbarIcon.TrayPopup>
tb:TaskbarIcon>
Grid>
Window>
自定义气球控件的实现
<UserControl x:Class="NotifyIconTest.SimpleCustomBalloon" ......>
<Grid>
<Border Width="100" Height="80" CornerRadius="10" Background="White">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition/>
Grid.ColumnDefinitions>
<Image Source="/Icons/bitbug_favicon.ico"/>
<TextBlock Grid.Column="1" Text="内容展示" VerticalAlignment="Center" HorizontalAlignment="Center"/>
Grid>
Border>
Grid>
UserControl>
事件的实现
private void Button_Click(object sender, RoutedEventArgs e)
{
SimpleCustomBalloon customBalloon =new SimpleCustomBalloon();
MyNotifyIcon.ShowCustomBalloon(customBalloon, PopupAnimation.Slide, 2000);
}
NotifyIcon以较为简洁的方式提供了Command
支持,在TaskbarIcon
中有LeftClickCommand
和DoubleClickCommand
两个命令属性,可以直接通过绑定表达式与ICommand
命令对象进行绑定,也就是用法跟Wpf的Command
属性一样的,只不过分为左键点击触发和双击触发。
TaskbarIcon
中的LeftClickCommandParameter
和DoubleClickCommandParameter
属性可以给两个对应的命令进行设置参数。LeftClickCommand
相对于DoubleClickCommand
而言有一个短暂的延时后才触发,这是因为判断是否双击需要有一定的时间间隔。创建自定义命令
public class ShowMessageCommand : ICommand
{
public event EventHandler? CanExecuteChanged;
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
MessageBox.Show(parameter.ToString());
}
}
命令绑定
<Window ......
xmlns:tb="http://www.hardcodet.net/taskbar"
......>
<Window.Resources>
<local:ShowMessageCommand x:Key="ShowMessageCommand"/>
</Window.Resources>
<Grid>
<tb:TaskbarIcon IconSource="/Icons/bitbug_favicon.ico" ToolTipText="系统托盘图标测试"
LeftClickCommand="{StaticResource ShowMessageCommand}"
LeftClickCommandParameter="命令触发成功"/>
</Grid>
</Window>
在TaskbarIcon
控件中对其成员使用数据绑定有两种方式,即隐式数据绑定和显式数据绑定。
隐式数据绑定的用法跟原生的WPF数据绑定差不多,也是根据层级查找DataContext
,具有以下规则:
TaskbarIcon
中的子控件进行数据绑定时,会查找自己DataContext
中的对应属性,如果本身没有设置DataContext
时,会向上层寻找DataContext
,在到达TaskbarIcon
控件之前,如果找到DataContext
,则将其作为自己的DataContext
使用。(这里跟原生WPF一样的)TaskbarIcon
控件时,如果TaskbarIcon
控件有设置DataContext
,就会将DataContext
作为TaskbarIcon
属性所赋值的控件对象的DataContext
,例如下面示例中的TrayToolTip
属性所赋值的控件是Border
(而不是TextBlock
)。TaskbarIcon
控件时,如果TaskbarIcon
控件没有设置DataContext
,就会将自己作为TaskbarIcon
属性所赋值的控件对象的DataContext
,例如下面示例中的TrayToolTip
属性所赋值的控件是Border
(而不是TextBlock
)。<Window ......
xmlns:tb="http://www.hardcodet.net/taskbar"
......>
<Grid>
<tb:TaskbarIcon IconSource="/Icons/bitbug_favicon.ico" ToolTipText="数据绑定">
<tb:TaskbarIcon.TrayToolTip>
<Border Width="80" Height="50" Background="White">
<TextBlock Text="{Binding Path=ToolTipText}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
Border>
tb:TaskbarIcon.TrayToolTip>
tb:TaskbarIcon>
Grid>
Window>
显式数据绑定需要通过使用依赖附加属性ParentTaskbarIcon
来实现,如果工具提示、弹出窗口、上下文菜单或自定义气球由TaskbarIcon
管理,则TaskbarIcon
通过此附加属性分配自己。
简单点说,就是TaskbarIcon
控件的子控件可以通过TaskbarIcon.ParentTaskbarIcon
依赖附加属性来获取TaskbarIcon
控件的属性。
实现方式
<Window ......
xmlns:tb="http://www.hardcodet.net/taskbar"
......>
<Grid>
<tb:TaskbarIcon IconSource="/Icons/bitbug_favicon.ico" DataContext="WPF IS GREAT: " ToolTipText="ToolTipText" >
<tb:TaskbarIcon.TrayToolTip>
<Border Width="200" Height="50" Background="White">
<TextBlock Text="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=Self},Path=(tb:TaskbarIcon.ParentTaskbarIcon).ToolTipText}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
TextBlock>
Border>
tb:TaskbarIcon.TrayToolTip>
tb:TaskbarIcon>
Grid>
Window>
TaskbarIcon
中提供了一组路由事件,例如TrayMouseMove
、TrayLeftMouseDown
、TrayToolTipOpen
等等很多很多事件,基本上囊括了在NotifyIcon或相关控件中发生的每件事。其使用方式跟原生WPF的事件是一样的,关于怎么使用这些事件实现动画效果的具体细节,可以查看官网的相关示例,这里就不做笔录了。