在 VB.NET 中创建系统托盘应用程序






4.94/5 (72投票s)
编写一个在通知区域启动的应用程序。
引言
这篇文章的灵感来自于 Quick Answers 中关于如何编写一个在托盘中启动的 VB.NET 应用程序的提问。CodeProject 上有很多此类示例——特别是成员 [ICR] 的这篇博文[^]——但我没有找到任何 VB 语言的例子。本文旨在解决这一情况。
理论
众所周知,C# 应用程序的启动是通过调用 System.Windows.Forms.Application.Run
来实现的。VB.NET 程序员通常无法直接看到此调用,因为编译器会在后台提供所需的代码。但是,我们可以显式调用它,这样就可以编写不依赖启动窗体的应用程序了。
AppContext 类
第一步是创建一个继承自 System.Windows.Forms.ApplicationContext
的类。它提供了操作系统管理您的应用程序所需的信息。您还可以在此处实例化系统托盘的 NotifyIcon
、用于与图标交互的菜单以及其他必需项。
Public Class AppContext
Inherits ApplicationContext
#Region " Storage "
Private WithEvents Tray As NotifyIcon
Private WithEvents MainMenu As ContextMenuStrip
Private WithEvents mnuDisplayForm As ToolStripMenuItem
Private WithEvents mnuSep1 As ToolStripSeparator
Private WithEvents mnuExit As ToolStripMenuItem
#End Region
#Region " Constructor "
Public Sub New()
'Initialize the menus
mnuDisplayForm = New ToolStripMenuItem("Display form")
mnuSep1 = New ToolStripSeparator()
mnuExit = New ToolStripMenuItem("Exit")
MainMenu = New ContextMenuStrip
MainMenu.Items.AddRange(New ToolStripItem() {mnuDisplayForm, mnuSep1, mnuExit})
'Initialize the tray
Tray = New NotifyIcon
Tray.Icon = My.Resources.TrayIcon
Tray.ContextMenuStrip = MainMenu
Tray.Text = "Formless tray application"
'Display
Tray.Visible = True
End Sub
#End Region
#Region " Event handlers "
Private Sub AppContext_ThreadExit(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.ThreadExit
'Guarantees that the icon will not linger.
Tray.Visible = False
End Sub
Private Sub mnuDisplayForm_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles mnuDisplayForm.Click
ShowDialog()
End Sub
Private Sub mnuExit_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles mnuExit.Click
ExitApplication()
End Sub
Private Sub Tray_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Tray.DoubleClick
ShowDialog()
End Sub
#End Region
End Class
这相当直接。图标和菜单在类的 Sub New
中初始化,图标被设为可见。ThreadExit
事件用于清理在类中创建的对象。该类还处理 mnuDisplay
(显示对话框窗体)和 mnuExit
(关闭应用程序)的 Click
事件。双击图标的效果与单击 mnuDisplay
相同。
OtherMethods 模块
我将 ExitApplication
和 ShowDialog
方法放在了单独的代码文件中。
Friend Module OtherMethods
Private PF As PopupForm
Public Sub ExitApplication()
'Perform any clean-up here
'Then exit the application
Application.Exit()
End Sub
Public Sub ShowDialog()
If PF IsNot Nothing AndAlso Not PF.IsDisposed Then Exit Sub
Dim CloseApp As Boolean = False
PF = New PopupForm
PF.ShowDialog()
CloseApp = (PF.DialogResult = DialogResult.Abort)
PF = Nothing
If CloseApp Then ExitApplication
End Sub
End Module
该模块被标记为 Friend
以防止其被导出。ExitApplication
是清理 AppContext
外部任何对象的地点。您的应用程序通过调用 Application.Exit
来关闭,这将触发 AppContext
的 ThreadExit
事件。如果您有任何打开的窗体,Application.Exit
会像您期望的那样关闭它们。
ShowDialog
显示一个简单的对话框,允许用户取消窗体或关闭应用程序。我需要做一些额外的工作来确保只显示一个对话框:否则,单击 mnuDisplayForm
会轻松地生成多个窗体。
主方法
下一步是编写启动应用程序的代码。在一个公共模块中,声明 Main
方法的四种变体之一。
Public Module LaunchApp
'Version 1
'
Public Sub Main()
Application.EnableVisualStyles()
Application.Run(New AppContext)
End Sub
'Version 2
'
'Public Sub Main(ByVal cmdArgs() As String)
'End Sub
'Version 3
'
'Public Function Main() As Integer
'End Function
'Version 4
'
'Public Function Main(ByVal cmdArgs() As String) As Integer
'End Function
End Module
在大多数情况下,版本 1 就足够了。如果您需要访问命令行,那么您应该使用版本 2。版本 3 和 4 允许您在应用程序关闭时向操作系统返回一个整数值,这只在很少的情况下有用。
这里的关键是调用 Application.Run
。它有三种重载:除了 ApplicationContext
,您还可以传递一个 Form
对象或不带参数。不带参数的版本会创建一个默认上下文,这对于控制台应用程序很有用。Form
版本是 VB 通常在内部使用的版本。您可以有条件地调用不同版本的 Application.Run
,如下所示:
Public Sub Main(ByVal cmdArgs() As String)
Application.EnableVisualStyles()
Dim UseTray As Boolean = False
For Each Cmd As String In cmdArgs
If Cmd.ToLower = "-tray" Then
UseTray = True
Exit For
End If
Next
If UseTray Then
Application.Run(New AppContext)
Else
Application.Run(New MainForm)
End If
End Sub
这样,您就可以使用命令行开关来决定您的应用程序是启动一个主窗体还是进入通知区域。
请注意,第一行代码是 Application.EnableVisualStyles
。由于原因将在下面解释,所以在配置项目时需要关闭视觉样式;这里是重新开启它们的地方。当然,您不必这样做,但如果您的应用程序有任何界面元素,这样做会使它们看起来更好看。
我的项目
最后一步是配置您的项目以使用 Main
方法。首先,打开 **我的项目** 界面。在“应用程序”选项卡上,取消选中“启用应用程序框架”复选框。这将禁用各种应用程序框架属性,包括 XP 视觉样式(这就是为什么我们需要重新开启它们)。现在,转到“启动对象”下拉菜单,选择 Sub Main
。请注意,即使您使用的是 Function Main
方法之一,您也会选择 Sub Main
。
当您运行应用程序时,引导程序现在将调用 Main
。您的自定义上下文类将被启动,它将在通知区域设置一个交互式图标。
结论
希望您觉得这篇文章有用;如果觉得有用,请投票支持。如果您发现任何错误,请在下方告知我,我将尽力修复它们。
历史
- 版本 4 - 2010 年 5 月 4 日 - 澄清了一些观点,修正了一些语法,并将“托盘”的引用改为“通知区域”(官方名称)或“系统托盘”(微软表示不正确,但在其自身文档中仍在使用)。对于命名上的混淆,我深表歉意。
- 版本 2、3 - 2010 年 4 月 26 日 - 首次发布。