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






4.65/5 (34投票s)
一个 WPF 任务栏“弹出”窗口。

引言
此 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
会被停止,DoubleAnimation
的 To
和 Duration
属性会被修改,然后 Storyboard
会被设置为再次 Begin
。例如,如果窗口正在 Hiding
并且用户将鼠标移到窗口上,窗口会停止隐藏并将其状态更改为 Opening
。Duration
是根据当前窗口位置计算的,以确保所需的打开/隐藏速率。
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 日:快速更新以修复一些视觉问题