【WinUI 3】用H.NotifyIcon显示系统任务栏(托盘)图标,实现程序后台运行

发布于:2022-12-22 ⋅ 阅读:(297) ⋅ 点赞:(0)

前言&问题背景

Windows平台上很多需要持续后台运行的程序,都有显示任务栏图标的需求,主要来显示状态信息、提供功能菜单栏。但UWP或Windows App SDK并没有提供对应的API,相反可能是在有意的抑制此项功能,我有理由怀疑是微软的战略布局。

但很显然,这项功能目前并不能有其它完美的替代解决方案,所以需要借助其它手段调用Win32 API实现。确实如此,GitHub中H.NotifyIcon库就帮助我们在WPF和WinUI平台上实现了。我们只需使用其NuGet包即可显示自己的任务栏图标。

但请注意,UWP程序强调生命周期管理,在后台期间会自动挂起。在官方的文档中,UWP确实有“在后台无限期运行”的解决方案,但一经使用则不能发布到Microsoft Store。为了响应其设计理念、也因为作者确实此方面涉足不深,本篇文章不探讨UWP平台的任务栏图标功能或后台运行功能。

解决方案

在NuGet源中,找到H.NotifyIcon包,根据你的需要选择H.NotifyIcon.WinUIH.NotifyIcon.Uno等包并安装。
注意,根据项目实例和自述文件,可以有多种声明与使用方法

  1. 直接全部在MainWindow内声明
<Window 
	...
	xmlns:tb="using:H.NotifyIcon"
	>
	<Grid>
		<Grid.Resources>
	        <MenuFlyout x:Key="TrayContextFlyout">
	            <MenuFlyoutItem Command="{StaticResource XamlUICommand类指令}" />
	            <MenuFlyoutSeparator />
	            <MenuFlyoutItem Command="{StaticResource XamlUICommand类指令}" />
	        </MenuFlyout>
	    </Grid.Resources>
	    <tb:TaskbarIcon
	    	x:Name="TrayIconView"
	        ToolTipText="悬停时显示的提示语文字,如ToolTip"
	        IconSource="图标文件路径,如/Images/TrayIcons/Logo.ico"
	        ContextMenu="ContextMenu资源,如{StaticResource TrayMenu}"
	        MenuActivation="显示菜单栏的行为,如LeftOrRightClick"
	        TrayPopup="弹出窗口资源,如{StaticResource TrayStatusPopup}"
	        PopupActivation="显示弹出窗口的行为,如DoubleClick"
	        LeftClickCommand="左键单击执行的命令,如{StaticResource Command}"
	        ContextMenuMode="SecondWindow"
	        NoLeftClickDelay="True"
	        />
	</Grid>
</Window>
  1. 定义一个UserControl,再在MainWindow使用
<Window 
   ...
   xmlns:views="using:UserControl所在命名空间"
   >
   <Grid>
   		...
   		<views:TrayIconView x:Name="TrayIconView" />
   </Grid>
</Window>
<UserControl ... >
    <UserControl.Resources>
       <MenuFlyout x:Key="TrayContextFlyout">
	        <MenuFlyoutItem Command="{StaticResource XamlUICommand类指令}" />
	        <MenuFlyoutSeparator />
	        <MenuFlyoutItem Command="{StaticResource XamlUICommand类指令}" />
	    </MenuFlyout>
    </UserControl.Resources>

    <tb:TaskbarIcon
	        ToolTipText="悬停时显示的提示语文字,如ToolTip"
	        IconSource="图标文件路径,如/Images/TrayIcons/Logo.ico"
	        ContextMenu="ContextMenu资源,如{StaticResource TrayMenu}"
	        MenuActivation="显示菜单栏的行为,如LeftOrRightClick"
	        TrayPopup="弹出窗口资源,如{StaticResource TrayStatusPopup}"
	        PopupActivation="显示弹出窗口的行为,如DoubleClick"
	        LeftClickCommand="左键单击执行的命令,如{StaticResource Command}"
	        ContextMenuMode="SecondWindow"
	        NoLeftClickDelay="True"
	        />
</UserControl>

个人更推荐第二种方法,这样不让主窗口文件显得臃肿,方便维护。
注意替换以上文字部分,要自行在Resources部分定义XamlUICommand指令并在MenuFlyoutItem中使用。

注意:在退出(Window Closed或自定义退出命令中)程序时,要调用TrayIconView.Dispose()方法。
注意:在退出(Window Closed或自定义退出命令中)程序时,要调用TrayIconView.Dispose()方法。
注意:在退出(Window Closed或自定义退出命令中)程序时,要调用TrayIconView.Dispose()方法。

这里补充说明一下以上文字部分的注释:

  • XamlUICommand类指令,直接定义在菜单栏中显示的图标、标签内容等的自定义命令,可在WinUI示例代码找到说明。
  • ContextMenuMode="SecondWindow"指定了菜单栏显示模式。“SecondWindow”指创建新的窗口覆盖Win32菜单栏,以使用WinUI风格设计的菜单栏。
    注意:作者在指定到本模式时,在Window代码中注册Activated等事件会产生运行时错误,因为H.NotifyIcon也会对UI元素事件进行注册,见issue #37。因此你需要将注册到这些事件的代码移动到App.xaml.cs或删除本项属性,则会默认仅使用Win32风格菜单栏(不自动再创建窗口)。
    Second Window 模式

至此,题目的问题已经得到解决。
本篇博客有待更新,H.NotifyIcon提供了更多实用方法和属性,包括XAML生成托盘图标、以效率模式隐藏窗口到后台等,读者可以自行从文首的GitHub链接到仓库探索。

总结&反思

WinUI没有提供系统托盘图标的API,所以要调用H.NotifyIcon这类库来实现。
我本人十分欣赏这个项目,它可以用窗口覆盖的形式,应用WinUI风格的菜单栏到托盘图标,看起来符合Fluent Design设计语言,而不是老式的Win32风格。

非常感谢H.NotifyIcon的作者,从本项目可以看见很多可以让项目更符合Windows 11语言的新奇点子,暂时介绍不完了。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到