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

无窗体系统托盘应用程序

2011年11月25日

CPOL

7分钟阅读

viewsIcon

262018

downloadIcon

28031

创建一个无窗体系统托盘应用程序。

SystemTrayApp.jpg

引言

本文旨在向您展示如何创建一个无窗体系统托盘应用程序。如果您使用窗体,可以从工具箱中附加一个 NotifyIconContextMenu 对象,但您可能发现需要创建一个无窗体版本,或者可能只是想知道如何实现。本文旨在说明如何做到这一点。

入门

在正常情况下,您会启动 Visual Studio,创建一个新的 WinForms 项目,将一些控件拖放到 Form1.cs 上,编译并运行。但是,您可能会发现您想知道如何只用代码创建系统托盘应用程序,并且没有窗体(例如,一个从菜单调用的“关于”对话框)。

初始步骤

  1. 创建一个新的 Windows 窗体应用程序。
  2. 从项目中删除 Form1.cs
  3. 打开 Program.cs - 删除 Application.Run(new Form1()); 这一行。
  4. 现在您将添加如下所示的类和对象 - 包含一个完整的可运行示例应用程序供您剖析。

如果您想查看所有代码并运行应用程序,请下载示例 - 在 bin/debug 中有一个可执行文件可以运行。

然而,重要的部分是以下内容

核心机制

首先,我们需要一个类来创建我们的系统托盘图标。如果您是从头开始,您现在就会创建它:我将我的命名为 ProcessIcon.cs。我还想继承自 IDisposable:这使我们有机会处理自己的清理工作,并确保程序关闭时干净利落。

为此,我们需要在代码中创建一个 NotifyIcon 对象。由于我们将在类中的多个位置访问该对象,因此我们将其声明为私有成员,以便我们可以在稍后的代码中实例化和使用它。

这就像在类中添加以下内容一样简单

NotifyIcon ni;

我们在类构造函数中实例化对象,如下所示

ni = new NotifyIcon();

我们现在有一个可以使用的 NotifyIcon 对象。

我这样做第一个原因是我想自己处理 Dispose 方法,以确保当应用程序关闭时,图标会立即从系统托盘中移除。您不需要这样做,但如果您不这样做,图标会一直停留在系统托盘中,直到您将鼠标光标移到它上面才会消失。

这样做的另一个原因是我们可以利用 using 模式,这样您就可以用以下代码替换 Program.cs Main 中的代码

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

// Show the system tray icon.
using (ProcessIcon pi = new ProcessIcon())
{ 
    pi.Display();

    // Make sure the application runs!
    Application.Run();
}

这确保应用程序将优雅地启动和退出:如果您以前没有见过 using 模式,请点击此处获取更多信息:using 语句 (C# 参考)

正如您所看到的,我还使用了 pi.Display();。此方法负责在系统托盘上显示图标并添加菜单,您可以从中创建或插入适当的选项(我将根据您的具体要求进行处理)。

其中最重要的部分之一是提供一个处理程序,用于处理对图标的鼠标单击。出于此演示的目的,我只关注鼠标左键单击,但您也可以截取右键和中键。然而,请注意 ContextMenuStrip 通常是在鼠标右键单击时调用的,因此您通常不需要对它做任何事情。

还需要注意的是,您打算使用的图标是嵌入的。您可以通过打开 Resources.resx(位于项目的 Properties 文件夹中)并将您选择的图标(已包含在项目中)拖放到该文件上。它会自动将其设置为图标资源。我提供了一个来自 VS 2010 图标集的图标 - 欢迎您替换为自己的图标。

现在我们需要添加一个类来处理我们需要的上下文菜单,以赋予应用程序所需的功能。请注意,您可能不需要任何这些功能,并且您的应用程序具有不需要任何交互的其他目的。在此假设下,让我们看看如何用代码创建上下文菜单并将其附加到应用程序。

您会注意到 ProcessIcon.Display 中的以下行用于将菜单附加到 NotifyIcon 对象

ni.ContextMenuStrip = new ContextMenus().Create();

菜单是在 ContextMenus.cs 中构建的。基本过程是创建 ToolStripMenuItemToolStripSeparator,然后将其附加到 ContextMenuStrip。从代码中可以看出,您需要向项目中添加一些东西才能使其做任何有意义的事情。

它应该有一个显示名称和一个事件处理程序(用于响应鼠标单击)。图像是可选的,尽管我为每个选项添加了一个(而且,不,我不知道我从哪里获得了示例中提供的图像)。与图标的规则相同 - 拖放到 Resources.resx 并使用

item.Image = Resources.About;

同样,每个项目都需要一个事件处理程序(如果您需要响应鼠标单击或执行其他操作)。这些可以添加为

item.Click += new EventHandler(Explorer_Click);

一个快速的方法是输入 item.Click += 并按两次 Tab 键。VS 会为您创建一个适当的处理程序。

在这个例子中,我只是依次添加了每个项目。您可以使用以下方式精确放置它们

menu.Items.Insert(index, item);

其中 index 是一个整数,表示该项目在列表中的位置。

谜题的最后一部分是每个菜单选项的事件处理程序。正如您在代码中看到的,每个选项都执行一个特定任务,例如退出应用程序(非常重要的选项!)或显示一个窗体——在本例中是一个通用的“关于”对话框。还要注意实例化和显示“关于”对话框的构造。这样做是为了解决您无法将“关于”对话框绑定到父窗体(因为没有父窗体)的问题,否则您可能会一直点击菜单选项,屏幕上就会充满“关于”对话框!这没什么帮助!这段代码解决了这个问题。

if (!isAboutLoaded)
{
    isAboutLoaded = true;
    new AboutBox().ShowDialog();
    isAboutLoaded = false;
}

所有这一切都是说,如果“关于”窗体没有加载,则将一个变量(boolean)设置为 true,然后加载窗体。当它关闭(并将控制权返回给此方法)时,取消设置该变量并重新开始。

如果您正在显示外部应用程序(如记事本、Windows 资源管理器或 Firefox),则不需要这样做,但对于属于应用程序的任何窗体,都应该这样做;即,如果窗体是应用程序的一部分,此功能可确保它只能加载和显示一次。

请注意,您需要为每个窗体设置一个布尔变量,否则只会打开您点击的第一个窗体!

示例中的其余代码应该相当清楚——您可以随意剖析、丢弃或窃取。我有空的时候会尽力回复任何疑问等。

结论

该代码包含打开窗体(“关于”对话框)、启动和显示 Windows 资源管理器以及退出应用程序的示例。用于打开 Windows 资源管理器的代码使用非常简单,可以调用它来打开几乎任何其他应用程序。该示例非常简单——请阅读本文以更深入地了解此非常有用的功能:Process

就是这样:您现在拥有一个应用程序,它将在系统托盘区域加载一个图标,并允许用户选择菜单选项并提供默认的鼠标左键操作。

警告!

请注意,所提供的代码力求简洁明了。您应确保提供适当的异常处理,并进行反复测试,以确保其按预期运行。

此应用程序是使用 Visual Studio 2010 和 .NET 4.0 在 Windows 7 中创建的。它尚未在任何其他环境中进行测试,虽然它应该可以在 Windows XP 和 Vista 中运行,但您有责任在使用前进行彻底测试。

历史

这是此代码的第一个也是最后一个版本,除非有人提供必须添加的更改,因为它们会大幅改进——您将获得感谢和引用。

© . All rights reserved.