65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (72投票s)

2010 年 4 月 26 日

CPOL

4分钟阅读

viewsIcon

308706

downloadIcon

20769

编写一个在通知区域启动的应用程序。

TrayApp

引言

这篇文章的灵感来自于 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 模块

我将 ExitApplicationShowDialog 方法放在了单独的代码文件中。

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 来关闭,这将触发 AppContextThreadExit 事件。如果您有任何打开的窗体,Application.Exit 会像您期望的那样关闭它们。

Dialog.jpg

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 日 - 首次发布。
© . All rights reserved.