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

WPF NotifyIcon

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (476投票s)

2009年5月16日

CPOL

16分钟阅读

viewsIcon

2010711

downloadIcon

48835

一个利用平台多项功能的 WPF NotifyIcon。

最新和最好的

引言

这是 WPF 平台下 NotifyIcon(系统托盘图标)的另一种实现方式。它不仅依赖于相应的 WinForms 组件,而且是一个完全独立的控件,利用 WPF 框架的多种特性来显示丰富的工具提示、弹出窗口、上下文菜单和气球消息。

wpf_notifyicon/ToolTip.png

背景

在我开发 NetDrives 工具(顺便说一句,它也是开源的)时,我发现 WPF 命名空间中没有内置的 NotifyIcon - 我不得不回退到 Windows Forms 命名空间中的组件。到目前为止一切都还好,但我很快就开始怀念许多功能,包括:

  • 丰富的工具提示,而非纯文本
  • WPF 上下文菜单和弹出窗口
  • 命令支持和路由事件
  • 灵活的数据绑定
  • 丰富的气球消息,而非操作系统提供的默认消息

面对这个问题,我开始着手开发这个纯 WPF 解决方案来填补这些空白。现在,我们有了。 :)

目录

文章和示例

本文简要讨论了控件的实现和结构,然后描述了各种功能。教程从常用内容(声明、设置图标、工具提示等)开始,然后深入研究稍微高级一些的场景(数据绑定、命令、事件)。

由于控件功能众多,这变成了一篇相当长的文章。不过,您应该能够立即上手。您可以逐步探索不同的功能,附件中的示例应用程序紧随本文的内容。

基本上,您可以立即尝试本文教程部分中概述的所有内容,并且您将在示例应用程序的教程文件夹中找到所有源代码。

wpf_notifyicon/Solution.png

项目页面 / 替代方案

项目页面

本文已有几年历史,虽然我会在此处保持文档的最新状态,但控件开发的参考页面可在 https://github.com/hardcodet/wpf-notifyicon 找到。

替代项目

我还要提到,市面上有很多替代解决方案。到目前为止,我看到的几乎所有都基本上是 WinForms NotifyIcon 的包装器,它们提供了您可能从 WinForms 项目中知道的基本功能。一个不完整的值得注意的链接列表:

控件实现概述

基本上,实现可以分为两个方面:

  • Interop 命名空间中的类包含通过 Win32 API 的 Shell_NotifyIcon 函数配置 NotifyIcon 的代码。这些类由控件内部使用。
  • 项目文件夹中的类提供 WPF API。这是您将使用的 public API。这里的核心是 TaskbarIcon 类,它代表您的 NotifyIcon

wpf_notifyicon/Classes.png

Win32 API - 互操作命名空间

与 WinForms NotifyIcon 一样,此控件基本上是 Windows API 的 Shell_NotifyIcon 函数的包装器。但是,它支持最新的改进(直到 Vista),包括更丰富的工具提示(带有 Windows XP 的回退机制)和气球消息的自定义图标。

如果您想开始自己的实现(无论是 WPF 还是其他),您基本上可以采用 Interop 命名空间的代码以及 Util 类中的帮助方法,这样就可以顺利进行了:那里的类和 enums 为您提供了 Shell_NotifyIcon 和相关类的干净且文档良好的外观。如果您想调用 Shell_NotifyIcon,可以通过 Util 类中的 WriteIconData 方法来实现。

/// Updates the taskbar icon with data provided by a given
/// <see cref="NotifyIconData"/> instance.
/// </summary>
/// <param name="data">Configuration settings for the NotifyIcon.</param>
/// <param name="command">Operation on the icon (e.g. delete the icon).</param>
/// <returns>True if the data was successfully written.</returns>
/// <remarks>See Shell_NotifyIcon documentation on MSDN for details.</remarks>
public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command)
{
  return WriteIconData(ref data, command, data.ValidMembers);
}

/// <summary>
/// Updates the taskbar icon with data provided by a given
/// <see cref="NotifyIconData"/> instance.
/// </summary>
/// <param name="data">Configuration settings for the NotifyIcon.</param>
/// <param name="command">Operation on the icon (e.g. delete the icon).</param>
/// <param name="flags">Defines which members of the <paramref name="data"/>
/// structure are set.</param>
/// <returns>True if the data was successfully written.</returns>
/// <remarks>See Shell_NotifyIcon documentation on MSDN for details.</remarks>
public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command,
                                 IconDataMembers flags)
{
  //do nothing if in design mode
  if (IsDesignMode) return true;

  data.ValidMembers = flags;
  lock (SyncRoot)
  {
    return WinApi.Shell_NotifyIcon(command, ref data);
  }
} 

WPF API - TaskbarIcon 类

作为控件的用户,您不必处理任何 Win32 内部细节。TaskbarIcon 类代表 NotifyIcon,并公开了一个干净的 API,这对于所有 WPF 开发人员来说应该都很熟悉。

以下是该类的属性和事件概述:

wpf_notifyicon/TaskbarIcon.png

有大量的属性和事件可用,但不用担心——如果您不想,您不必处理其中的大部分。基本上,控件在以下方面为您提供了功能,这些功能全部是可选的(即使是显示的图标本身):

  • 在托盘区域显示图标
  • 当用户将鼠标悬停在托盘图标上时显示工具提示
  • 当用户单击 NotifyIcon 时打开 ContextMenu
  • 当用户单击 NotifyIcon 时打开一个交互式 Popup 控件
  • 在事件发生时(标准气球提示或自定义气球)在托盘区域显示气球消息

教程 第一部分:基础知识

你好 NotifyIcon

首先,让我们看看如何创建您的第一个 NotifyIcon。您可以以编程方式和声明方式进行。

在代码中创建 NotifyIcon

//Note: XAML is suggested for all but the simplest scenarios
TaskbarIcon tbi = new TaskbarIcon();
tbi.Icon = Resources.Error;
tbi.ToolTipText = "hello world";

在 XAML 中创建 NotifyIcon

为了在 XAML 中声明 TaskbarIcon,您首先需要在 XAML 文件的头部添加以下命名空间声明:

xmlns:tb="http://www.hardcodet.net/taskbar" 

然后只需使用 tb 前缀声明 TaskbarIcon

<Window
  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"
>
  <Grid>

    <!--
      in order to create a NotifyIcon, all you need is the
      namespace declaration (see above on line 4) and a simple
      declaration
    -->
    <tb:TaskbarIcon
      IconSource="/Icons/Error.ico"
      ToolTipText="hello world" />

  </Grid>
</Window>

上面的代码片段是您在托盘区域显示以下 NotifyIcon 所需的所有内容:

wpf_notifyicon/BasicDeclaration.png

注意:如果您仔细看了,会发现代码中设置了 Icon 属性,而在 XAML 中,图标图像是通过 IconSource 依赖属性设置的。这两个属性的区别在于,Icon 是一个标准的属性,接受一个 System.Drawing.Icon 实例,而 IconSource 是一个类型为 ImageSource 的 WPF 依赖属性,更适合在 XAML 中使用。然而,结果是相同的:设置 IconSource 会自动设置 Icon 属性。

从资源字典创建 NotifyIcon

这基本上是代码和标记的结合,也是我推荐的实际场景的模式:

  • TaskbarIcon 在 XAML 资源字典中声明。
  • 它在应用程序初始化期间在代码中查找。

这种模式允许您将 NotifyIcon 与应用程序窗口分开,并允许您在应用程序仍在后台运行时关闭所有窗口。

<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">

  <!-- Globally declared notify icon -->
  <tb:TaskbarIcon x:Key="MyNotifyIcon"
                  IconSource="/Icons/Error.ico"
                  ToolTipText="hello world" />

</ResourceDictionary>

一旦声明为全局资源,您就可以使用 FindResource 方法来查找您的 NotifyIcon

public class App
{
  private TaskbarIcon tb;
  
  private void InitApplication()
  {
    //initialize NotifyIcon
    tb = (TaskbarIcon) FindResource("MyNotifyIcon");
  }
}

提供交互性的一个优雅的解决方案是将视图模型分配给 NotifyIcon 控件,方法是在 XAML 或以编程方式设置其 DataContext。请查看下载中的无窗口应用程序示例。

更改图标可见性

您可以通过设置 TaskbarIconVisibility 属性(默认设置为 Visibility.Visible)来随时在运行时显示/隐藏托盘区域中的 NotifyIcon。以下是如何在 XAML 中执行此操作:

<tb:TaskbarIcon
  IconSource="/Icons/Error.ico"
  Visibility="Collapsed" />

……这是对应的 C# 代码片段:

TaskbarIcon tbi = new TaskbarIcon();
tbi.Icon = Resources.Error;
tbi.Visibility = Visibility.Collapsed;

正确关闭 NotifyIcon

创建后,NotifyIcon 将保持活动状态(无论是否可见),直到 TaskbarIcon 类被释放。如果您关闭应用程序,这将自动发生,所以您可能根本不必担心(如果您只有一个 NotifyIcon,您可以将其隐藏,如果您不需要它)。但是,如果您想在运行时完全删除它,您应该调用 TaskbarIcon 类的 Dispose 方法。

教程 第二部分:工具提示、弹出窗口、上下文菜单、气球提示

教程的第二部分侧重于支持的各种视觉类型。从这里开始,我将只展示 XAML 中的示例。我认为一旦 UI 变得更复杂,以声明方式处理比以代码方式处理更有意义。

现在让我们来看一些精彩的内容。 :)

工具提示

wpf_notifyicon/ToolTip.png

(在 NetDrives 中提供快速状态摘要的动态工具提示。)

TaskbarIcon 类提供两个与工具提示相关的属性:

  • TrayToolTip 属性接受任意 UIElement,该元素将在用户将鼠标悬停在该区域时显示。它可以是用户控件、按钮、图像控件或任何其他控件。
    这是一种显示丰富工具提示的非常便捷的方式。但是,由于早期版本(XP / 2003)中 Win32 Shell_NotifyIcon 函数的限制,它们仅从 Windows Vista/2008 开始支持。当然,丰富的工具提示在 Windows 7 上也能正常工作。
  • ToolTipText 属性接受一个 string。它在两种情况下显示:
    • 如果未使用 TrayToolTip 属性
    • 在不支持丰富工具提示的旧操作系统(xp/2003)上
  • 因此,我建议您始终 设置 ToolTipText 属性,以便在您的应用程序运行在较旧的操作系统上时有一个回退机制。

请注意,仅当没有其他控件(Popup、ContextMenu 或自定义气球提示)当前显示时,才会显示工具提示。

注意:由于 TaskbarIcon 派生自 FrameworkElement,因此它还提供了一个 ToolTip 属性,该属性接受一个任意 object。此属性将被忽略 - 您只能使用 TrayToolTipToolTipText 属性!

工具提示创建 1:内联声明工具提示

您可以直接在 TaskbarIcon 声明中声明自定义 ToolTip。这是一个简单的示例,显示了一个半透明的 Border 和一个 TextBlock

<tb:TaskbarIcon
  IconSource="/Icons/Error.ico"
  ToolTipText="hello world">
  
  <!--
    We can use arbitrary UI elements as ToolTips.
    Let's use a semi-transparent border.
  -->
  <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>

这是结果

wpf_notifyicon/SimpleToolTip.png

工具提示创建 2:使用用户控件

虽然内联声明工具提示可行,但您可能希望将工具提示 UI 与 NotifyIcon 的声明分开。例如,让我们重新创建上面的 ToolTip,但这次将 UI 放入一个名为 SimpleUserControl 的专用用户控件中:

<UserControl
  x:Class="Samples.Tutorials.ToolTips.SimpleUserControl"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >

  <!-- a simple user control which displays a fixed text within a border -->
  <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> 

……最后将其分配给 NotifyIcon

<tb:TaskbarIcon
  IconSource="/Icons/Error.ico"
  ToolTipText="hello world">

  <!-- assign user control as ToolTip -->
  <tb:TaskbarIcon.TrayToolTip>
    <local:SimpleUserControl />
  </tb:TaskbarIcon.TrayToolTip>

</tb:TaskbarIcon> 

工具提示创建 3:链接到资源

如果您已将 ToolTip 控件声明为资源,则声明将变得更加简单:

<tb:TaskbarIcon
  IconSource="/Icons/Error.ico"
  ToolTipText="hello world"
  TrayToolTip="{StaticResource TheKeyOfMyToolTipControl}"  
  />

弹出窗口

ToolTip 不同,Popup 仅在用户单击 NotifyIcon 时显示。如果用户鼠标移出托盘区域,它将保持打开状态,但只要用户单击其他地方就会关闭。因此,您可以在 Popup 中提供交互式内容,通常是为了提供对应用程序的快速访问。

wpf_notifyicon/Popup.png

(NetDrives 中的弹出窗口,它提供了对所有已配置共享的快速访问。用户可以从网格中选择一个共享,双击一个共享,或打开网格的上下文菜单。)

声明 Popup 的方式与设置 ToolTip 几乎相同:您可以通过设置 TrayPopup 属性来将任意 UIElement 控件分配给 Popup。让我们重复上面的内联 ToolTip 示例,但这次显示一个 Button 而不是 TextBlock

<tb:TaskbarIcon
  IconSource="/Icons/Error.ico"
  ToolTipText="hello world">

  <!--
    We can use arbitrary UI elements as Popups.
    Popups stay open if the user moves away from the tray area
  -->
  <tb:TaskbarIcon.TrayPopup>
    <Border
      Background="White"
      BorderBrush="Orange"
      BorderThickness="2"
      CornerRadius="4"
      Width="160"
      Height="40">
      <Button
        Content="Click Me!"
        HorizontalAlignment="Center"
        VerticalAlignment="Center" />
    </Border>
  </tb:TaskbarIcon.TrayPopup>

</tb:TaskbarIcon>

上面的标记会生成以下(非常丑陋的)结果,只要用户单击 NotifyIcon。但至少,它是一个 Popup

wpf_notifyicon/SimplePopup.png

弹出窗口激活

默认情况下,当单击鼠标左按钮时会打开 Popup,但您可以使用 PopupActivation 属性来控制哪个鼠标按钮激活您的 Popup。您可以选择左、中、右鼠标按钮,或一些常见的组合。

<!-- also open popup on double click -->
<tb:TaskbarIcon
  TrayPopup="{StaticResource MyPopup}"  
  PopupActivation="LeftOrDoubleClick"
  /> 

wpf_notifyicon/PopupActivation.png

上下文菜单

当用户单击 NotifyIcon 时,您可以显示标准的 WPF 上下文菜单。

wpf_notifyicon/ContextMenu.png

这里没有什么特别的 - ContextMenu 属性直接继承自 FrameworkElement 基类。

<tb:TaskbarIcon
  IconSource="/Icons/Error.ico"
  ToolTipText="hello world">

  <!-- Set a simple context menu  -->
  <tb:TaskbarIcon.ContextMenu>
    <ContextMenu
      Background="LightCoral">
      <MenuItem Header="First Menu Item" />
      <MenuItem Header="Second Menu Item" />
    </ContextMenu>
  </tb:TaskbarIcon.ContextMenu>

</tb:TaskbarIcon>

上面的代码片段生成以下输出:

wpf_notifyicon/SimpleContextMenu.png

上下文菜单激活

默认情况下,当单击鼠标右按钮时会打开上下文菜单,但您可以使用 MenuActivation 属性来控制哪个鼠标按钮打开弹出窗口。

<!-- open menu on both left or right mouse click -->
<tb:TaskbarIcon
  ContextMenu="{StaticResource MyMenu}"  
  MenuActivation="LeftOrRightClick"
  />

注意:如果您为 PopupActivationMenuActivation 定义了冲突的值,上下文菜单将始终优先于 Popup

<!-- The popup will only show on double clicks - left click opens context menu -->
<tb:TaskbarIcon
  ContextMenu="{StaticResource MyMenu}"
  TrayPopup="{StaticResource MyPopup}"  
  MenuActivation="LeftOrRightClick"
  PopupActivation="LeftOrDoubleClick"
  />

气球提示

NotifyIcon 支持两种气球提示,您可以使用它们在托盘区域显示信息:

  • 标准气球,由操作系统定义。
  • 自定义气球 - 就像 ToolTipPopup 一样,您可以将任意 UIElement 转换为气球消息。这不仅意味着您可以按照自己的喜好样式化气球,而且由于 NotifyIcon 的丰富事件模型,您还可以创建精美的动画。

标准气球

为了显示标准气球,TaskbarIcon 类提供了两个 ShowBalloonTip 方法。一个显示带有标准图标(InfoWarningErrorNone)的气球,另一个重载则显示带有自定义 System.Drawing.Icon 的气球:

private void ShowStandardBalloon()
{
  string title = "WPF NotifyIcon";
  string text = "This is a standard balloon";

  //show balloon with built-in icon
  MyNotifyIcon.ShowBalloonTip(title, text, BalloonIcon.Error);

  //show balloon with custom icon
  MyNotifyIcon.ShowBalloonTip(title, text, MyNotifyIcon.Icon);


  //hide balloon
  MyNotifyIcon.HideBalloonTip();
}

这是使用自定义图标的第二个方法调用的输出:

wpf_notifyicon/StandardBalloon.png

自定义气球

为了显示自定义气球提示,调用 TaskbarIcon 类的 ShowCustomBalloon 方法。ShowCustomBalloon 不仅可以显示任意 UIElement 实例,还提供了一些内置的基本动画和一个可选的时间跨度,该时间跨度定义了气球的超时时间。

wpf_notifyicon/CustomBalloon.png

private void ShowCustomBalloon()
{
  FancyBalloon balloon = new FancyBalloon();

  //show balloon and close it after 4 seconds
  MyNotifyIcon.ShowCustomBalloon(balloon, PopupAnimation.Slide, 4000);
}

您还可以以编程方式关闭气球或中止关闭计时器(例如,当用户将鼠标悬停在气球上时)。有关一些场景,请参阅示例应用程序中的展示。

教程 第三部分:命令、事件和数据绑定

本教程的最后一部分不一定要求您是 WPF 大师,但假设您熟悉命令、路由事件或数据绑定的概念。如果您迷失了,请在论坛上提问,我(以及希望其他人)会尽力指引您。

内置命令支持

命令提供了一种在不挂钩事件监听器的情况下响应 NotifyIcon 上事件的简洁方式。TaskbarIcon 目前公开了两个属性,允许您分配一个命令:

  • LeftClickCommand
  • DoubleClickCommand

让我们实现一个简短的示例来使用它们……

实现自定义命令

首先,这是一个名为 ShowMessageCommand 的简单命令,它只是显示一个消息对话框。对话框文本取自命令参数:

/// <summary>
/// A simple command that displays the command parameter as
/// a dialog message.
/// </summary>
public class ShowMessageCommand : ICommand
{
  public void Execute(object parameter)
  {
    MessageBox.Show(parameter.ToString());
  }

  public bool CanExecute(object parameter)
  {
    return true;
  }

  public event EventHandler CanExecuteChanged;
} 

声明命令

有了 ShowMessageCommand,剩下的就是将其挂钩到 NotifyIcon。在下面的代码片段中,我将命令声明为本地资源:

<Window
  x:Class="Samples.Tutorials.Commands.CommandWindow"
  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"
  xmlns:local="clr-namespace:Samples.Tutorials.Commands"
  Height="300"
  Width="300">
  <Grid>
    
    <!-- declare the command as a local resource -->
    <Grid.Resources>
      <local:ShowMessageCommand
        x:Key="MessageCommand" />
    </Grid.Resources>

    <!-- declare the NotifyIcon and configure commands with parameters -->
    <tb:TaskbarIcon
      IconSource="/Icons/Error.ico"
      
      LeftClickCommand="{StaticResource MessageCommand}"
      LeftClickCommandParameter="Single left mouse button click."
      
      DoubleClickCommand="{StaticResource MessageCommand}"
      DoubleClickCommandParameter="Double click on NotifyIcon." />

  </Grid>
</Window>

您将在示例应用程序中找到此示例(以及本文中的所有其他代码片段)。

LeftClickCommand 延迟

请注意,LeftClickCommand 在短暂延迟后触发(与立即触发的 DoubleClickCommand 相反)。这是因为第一次单击和第二次单击之间存在一个时间间隔,操作系统才能将鼠标操作视为双击。NotifyIcon 足够智能,可以等待此期间,以确保仅在用户在此期间未单击第二次时才触发 LeftClickCommand

工具提示、弹出窗口、上下文菜单和自定义气球中的数据绑定

绑定到管理您的 ToolTipPopup、上下文菜单或气球提示的 TaskbarIcon 类非常简单。有两种变体(都在示例应用程序中显示):

  • 您可以通过自定义控件的数据上下文进行隐式绑定(如果 DataContext 尚未使用)。
  • 您可以通过附加的 ParentTaskbarIcon 属性进行显式绑定。即使 DataContext 已在使用中,此属性也允许您访问 TaskbarIcon

通过 DataContext 进行隐式绑定

为了简化您的数据绑定场景,TaskbarIcon 类会尝试根据以下规则设置 ToolTips、Popups、上下文菜单和自定义气球消息的 DataContext

  1. 如果 ToolTips、Popups、上下文菜单或自定义气球本身具有数据上下文,则不会更改。
  2. 如果 DataContext 未使用:
    1. 如果 TaskbarIcon 具有数据上下文(其自身的 DataContext 属性不为 null),它会将自己的数据上下文分配给 ToolTips、Popups、上下文菜单和自定义气球提示的 DataContext 属性。
    2. 如果 TaskbarIcon 没有数据上下文(其自身的 DataContext 属性为 null),它会将自身作为 ToolTips、Popups、上下文菜单和气球提示的 DataContext

此机制为您提供了一个非常简单的解决方案,可以在绑定表达式中隐式访问 TaskbarIcon 或其 DataContext。作为一个简单的例子,让我们回顾一下第一个教程中的内联 ToolTip 示例,但这次将显示的 TextBlock 的输出绑定到 NotifyIconToolTipText 属性:

<tb:TaskbarIcon
  IconSource="/Icons/Error.ico"
  ToolTipText="hello world">

  <tb:TaskbarIcon.TrayToolTip>
    <Border>
      <!-- use a binding expression rather than fixed text -->
      <TextBlock
        Text="{Binding Path=ToolTipText}"
        HorizontalAlignment="Center"
        VerticalAlignment="Center" />
    </Border>
  </tb:TaskbarIcon.TrayToolTip>

</tb:TaskbarIcon>

如果将上面的代码片段与第一个 ToolTip 教程进行比较,您会发现只有一行被更改了:

<TextBlock Text="{Binding Path=ToolTipText}" ... />

此绑定语句有效,因为 TaskbarIcon 类成为了 TextBlock 控件的 DataContext。运行时会发生这种情况:

  • Border 控件分配给 TaskbarIconTrayToolTip 属性。
  • NotifyIcon 检查此 Border 控件的 DataContext 是否已设置。事实并非如此。
  • 因此,TaskbarIcon 检查它自己的 DataContext 是否已设置。
  • 因为 TaskbarIcon 本身也没有 DataContext,所以它将自己分配为 Border 控件的 DataContext
  • TextBlockBorder 控件的子项。因此,它继承了 DataContext。相应地,它可以隐式绑定到 TaskbarIconToolTipText 属性。

显式绑定:ParentTaskbarIcon 附加属性

另一种更显式的解决方案是使用 ParentTaskbarIcon 附加属性。如果 ToolTipPopup、上下文菜单或自定义气球由 TaskbarIcon 管理,TaskbarIcon 会通过此附加属性自我分配。

您可以在代码或数据绑定表达式中访问此属性。例如,这是相同的 ToolTip 示例,这次通过 ParentTaskbarIcon 属性显式访问 NotifyIcon

<!-- This notifyicon has its DataContext set - implicit binding is not possible -->
<tb:TaskbarIcon
  x:Name="MyNotifyIcon2"
  DataContext="WPF IS GREAT: "
  IconSource="/Icons/Inactive.ico"
  ToolTipText="{Binding ElementName=txtToolTip, Path=Text}">

  <tb:TaskbarIcon.TrayToolTip>
  
    <!-- 
      Important: The attached property is assigned to the border!
      The NotifyIcon does not touch the underlying controls.
    -->
    <Border
      Background="White"
      BorderBrush="Orange"
      BorderThickness="2"
      CornerRadius="4"
      Opacity="0.8"
      Width="160"
      Height="40">
      <!-- Implicitly access the DataContext (which is a string this time)-->
      <TextBlock Text="{Binding}">
      <!-- Explicitly access the NotifyIcon -->
      <TextBlock
        Text="{Binding RelativeSource={RelativeSource FindAncestor,
                                       AncestorType={x:Type Border}},
               Path=(tb:TaskbarIcon.ParentTaskbarIcon).ToolTipText}"
        HorizontalAlignment="Center"
        VerticalAlignment="Center" />
      </TextBlock>
    </Border>
  </tb:TaskbarIcon.TrayToolTip>

</tb:TaskbarIcon> 

请注意绑定表达式,这里的语法稍微复杂一些,以便访问附加属性。TextBlock 需要解析其父 Border(已分配附加属性)才能访问附加属性。

<TextBlock
    Text="{Binding RelativeSource={RelativeSource FindAncestor,
                                  AncestorType={x:Type Border}},
    Path=(tb:TaskbarIcon.ParentTaskbarIcon).ToolTipText}"
 /> 

尽管增加了复杂性,但这种语法有其优势,因为即使 DataContext 正在使用中(例如,为了访问您的 ViewModel),附加属性也始终可用。

事件

路由 TaskbarIcon 事件

TaskbarIcon 类提供了一系列路由事件 - 几乎 NotifyIcon 或相关控件发生的任何事情都有一个事件。有关如何以声明方式使用这些事件来触发动画的示例,请查看示例应用程序中的教程。

wpf_notifyicon/EventTutorial.png

用于工具提示、弹出窗口和气球的附加事件

这是控件的一个炫酷 WPF 功能。基本上,附加事件是在您的自定义工具提示、弹出窗口和气球控件上触发的事件,而无需您编写一行代码。这是一种以纯声明方式触发这些控件中动画的非常简洁的机制。我在此不详述,而是指向这些资源:

  • 示例应用程序使用 PopupOpened 附加事件来在每次显示弹出窗口时触发弹出窗口中的动画(旋转图标)。
    自定义气球示例使用多个附加事件来淡入淡出。
  • 我最近发布了一个关于使用附加事件在 Blend 中触发 WPF 动画的通用教程,以及另一个示例。您可以在 这里 找到它。

结论

我希望这篇文章以及附带的示例能帮助您顺利上手。我真的很喜欢编写这个控件,希望它能成为您工具箱中有价值的补充。编码愉快! :)

历史

(详细的更改日志是解决方案的一部分)

1.0.5 (2013.11.20)
Fixes issues with x64, Win 8, Focus

1.0.4 (2009.09.21)
Proper focus for popups, improved compatibility for WinForms scenarios.

1.0.3 (2009.07.02)
Proper support for routed commands including command targets. 

1.0.2 (2009.05.18)
Fixed possibly wrong DataContext assignment when initializing ContextMenus.

1.0.1 (2009.05.15)
Initial CodeProject released 
© . All rights reserved.