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

WPF 任务栏通知器 - WPF 任务栏通知窗口

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.65/5 (34投票s)

2008年1月12日

CPOL

4分钟阅读

viewsIcon

318281

downloadIcon

17659

一个 WPF 任务栏“弹出”窗口。

Screenshot of the demo application

引言

此 WPF 任务栏通知器是一个使用故事板动画打开和关闭的 WPF 窗口。当发生某些有趣的事件时,它可以用于通知用户。

当调用 Notify() 时,窗口会从桌面右下角(在任务栏上方,如果任务栏存在的话)向上移动。打开后,窗口会保持打开状态一段可配置的时间,然后开始隐藏。打开和隐藏的速度也是可配置的。只要鼠标悬停在窗口上,窗口就会保持打开状态。

此外,还包含了一个 Windows Forms NotifyIcon 的 WPF 包装器,以便轻松地在系统托盘中添加图标。这个图标可以方便地为 WPF 任务栏通知器添加一个用于重新打开、配置和退出的用户界面。NotifyIcon 包装器由 Mariano Omar Rodriguez 编写,并发布在他的博客 此处

要查看 WPF 任务栏通知器的实际效果,请下载演示可执行文件并尝试一下!

背景

至少还有其他几个 C# 实现的从任务栏弹出的窗口。在我见过的里面,没有一个提供 WPF 窗口。

Using the Code

TaskbarNotifier.cs 包含 TaskbarNotifier 类,它是弹出式 WPF 窗口。TaskbarNotifier 不定义窗口内容,因为任何使用它的人都会希望定义自己的应用程序特定内容。在演示源代码中,ExampleTaskbarNotifier 类使用 TaskbarNotifier 作为基类,并在 ExampleTaskbarNotifier.xaml 中定义其内容。您决定在自己的 TaskbarNotifier 子类中包含什么内容,完全取决于您。

有几个公共属性和方法允许您控制窗口的行为。

  • int OpeningMilliseconds - 窗口从完全隐藏位置打开所需的时间(毫秒)。
  • int HidingMilliseconds - 窗口从完全打开位置隐藏所需的时间(毫秒)。
  • int StayOpenMilliseconds - 窗口达到完全打开位置后将保持打开状态的时间(毫秒)。一旦这段时间过去,它将开始隐藏。
  • int LeftOffset - 窗口应距桌面左侧的像素偏移量。这允许窗口水平移动,如果默认值(最右下角)不符合您的要求。
  • void Notify() - 告诉窗口打开。
  • void ForceHidden() - 立即将窗口置于隐藏位置。

如上所述,TaskbarNotifier 应该被继承以添加您自己的内容。在示例中,主应用程序窗口本身是一个标准窗口,而不是 TaskbarNotifier 窗口。主窗口在其构造函数中创建并显示 ExampleTaskbarNotifier 窗口。此时,它对用户不可见,因为它在桌面的位置太低而无法看到。

public Window1()
{
    this.taskbarNotifier = new ExampleTaskbarNotifier();
    InitializeComponent();
    this.taskbarNotifier.Show();
}

TaskbarNotifier 窗口的应用程序特定内容可以在 XAML 中定义,就像其他任何 WPF 窗口一样。

<tn:TaskbarNotifier x:Class="WPFTaskbarNotifierExample.ExampleTaskbarNotifier"
    xmlns:tn="clr-namespace:WPFTaskbarNotifier;assembly=WPFTaskbarNotifier"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Taskbar Notifier Example" Height="160"  Width="300"
    >

    <!-- insert your XAML here -->

</tn:TaskbarNotifier>

由于 TaskbarNotifier 窗口会完全隐藏自己,因此似乎有必要包括某种方式来打开、配置和退出它。我包含了 Mariano Omar Rodriguez 的 NotifyIcon 包装器,以便在 XAML 中实例化一个系统托盘图标。虽然演示应用程序不使用 NotifyIcon's 的气球提示,但它演示了如何将系统托盘图标与任务栏通知器结合使用。

以下是在示例 XAML 中声明 NotifyIcon 的方式

<tn:NotifyIcon x:Name="NotifyIcon" Text="Example Notifier" 
        Icon="Resources/UFO.ico" MouseDoubleClick="NotifyIcon_DoubleClick">
  <tn:NotifyIcon.ContextMenu>
    <ContextMenu>
      <MenuItem Header="Open" Click="NotifyIconOpen_Click" />
      <MenuItem Header="Configure..." Click="NotifyIconConfigure_Click" />
      <Separator/>
      <MenuItem Header="Exit" Click="NotifyIconExit_Click" />
    </ContextMenu>
  </tn:NotifyIcon.ContextMenu>
</tn:NotifyIcon>

在您的应用程序中,只要 TaskbarNotifier 弹出有意义,就可以调用其 Notify() 方法。

工作原理

TaskbarNotifier 使用一个基于窗口 Top 属性和 DoubleAnimation 的单一 Storyboard

this.animation = new DoubleAnimation();
Storyboard.SetTargetProperty(this.animation, new PropertyPath(Window.TopProperty));
this.storyboard = new Storyboard();
this.storyboard.Children.Add(this.animation);
this.storyboard.FillBehavior = FillBehavior.Stop;

通过动画化 Top,窗口的位置会移动。窗口的大小永远不会改变,因为这可能会改变窗口内容的组织方式。窗口只是被移出视图。

在代码中,当需要改变移动时,Storyboard 会被停止,DoubleAnimationToDuration 属性会被修改,然后 Storyboard 会被设置为再次 Begin。例如,如果窗口正在 Hiding 并且用户将鼠标移到窗口上,窗口会停止隐藏并将其状态更改为 OpeningDuration 是根据当前窗口位置计算的,以确保所需的打开/隐藏速率。

private DisplayState 属性驱动窗口的移动。DisplayState 的可能值在以下枚举中定义

private enum DisplayStates
{
    Opening,
    Opened,
    Hiding,
    Hidden
}

每当 DisplayState 被更改时,就会调用 OnDisplayStateChanged 方法

private DisplayStates DisplayState
{
    get { return this.displayState; }
    set
    {
        if (value != this.displayState)
        {
            this.displayState = value;

            // Handle the new state.
            this.OnDisplayStateChanged();
        }
    }
}

OnDisplayStateChanged 只是停止当前动画,并根据四种可能的 DisplayStates 中的每一种来处理 DisplayState

例如,当窗口被告知打开时,Storyboard 的配置和重新启动方式如下

// Because the window may already be partially open, the rate at which
// it opens may be a fraction of the normal rate.
// This must be calculated.
int milliseconds = this.CalculateMillseconds(this.openingMilliseconds, this.openedTop);

// Reconfigure the animation.
this.animation.To = this.openedTop;
this.animation.Duration = new Duration(new TimeSpan(0, 0, 0, 0, milliseconds));

// Set the specific completed event handler.
this.storyboard.Completed += arrivedOpened;

// Start the animation.
this.storyboard.Begin(this, true);

使用 DispatcherTimer 在窗口处于打开状态的时间达到 StayOpenMilliseconds 所指示的持续时间后触发隐藏。

以下是 DispatcherTimer 的创建方式

// Prepare the timer for how long the window should stay open.
this.stayOpenTimer = new DispatcherTimer();
this.stayOpenTimer.Interval = TimeSpan.FromMilliseconds(this.stayOpenMilliseconds);
this.stayOpenTimer.Tick += new EventHandler(this.stayOpenTimer_Elapsed);

stayOpenTimer 到期时,计时器会停止,并且只要鼠标不在其上方,就会指示窗口隐藏。这是通过将 DisplayState 设置为 DisplayStates.Hiding 来实现的。

private void stayOpenTimer_Elapsed(Object sender, EventArgs args)
{
    // Stop the timer because this should not be an ongoing event.
    this.stayOpenTimer.Stop();

    if (!this.IsMouseOver)
    {
        // Only start closing the window if the mouse is not over it.
        this.DisplayState = DisplayStates.Hiding;
    }
}

有关更多详细信息,请下载源代码并查看。

历史

  • 2007 年 12 月:初次创建
  • 2008 年 1 月 23 日:快速更新以修复一些视觉问题
© . All rights reserved.