Click here to Skip to main content
Click here to Skip to main content
Go to top

WPF Taskbar Notifier - A WPF Taskbar Notification Window

, 22 Jan 2008
Rate this:
Please Sign up or sign in to vote.
A WPF taskbar "popup" window.
Screenshot of the demo application

Introduction

This WPF Taskbar Notifier is a WPF window that opens and closes using a storyboard animation. It can be used to notify a user when some event of interest has occurred.

When Notify() is called, the window moves up from the bottom right corner of the desktop (just above the taskbar when the taskbar is there). Once open, the window stays open for a configurable amount of time before it begins hiding again. The speed for opening and hiding are also configurable. The window will remain open as long as the mouse is over it.

Additionally, a WPF wrapper for the Windows Forms NotifyIcon is included to make it easy to add an icon to the system tray. This icon can come in handy for adding a user interface for re-opening, configuring, and exiting the WPF Taskbar Notifier. The NotifyIcon wrapper was written by Mariano Omar Rodriguez and posted on his blog here.

To see the WPF Taskbar Notifier in action, download the demo executable and give it a try!

Background

There are at least several other C# implementations of a window that pops up from the taskbar. None of the ones I've seen offer a WPF window.

Using the Code

TaskbarNotifier.cs contains the TaskbarNotifier class, which is the popup WPF window. TaskbarNotifier defines no window content because anyone using it will want to define their own application specific content. In the demo source code, the ExampleTaskbarNotifier class uses TaskbarNotifier as a base class and defines its content in ExampleTaskbarNotifier.xaml. What content you decide to put into your own subclass of TaskbarNotifier is up to you.

There are several public properties and methods that allow you to control the window's behavior.

  • int OpeningMilliseconds - The number of milliseconds it takes for the window to open from its fully hidden position.
  • int HidingMilliseconds - The number of milliseconds it takes for the window to hide from its fully open position.
  • int StayOpenMilliseconds - The number of milliseconds the window will stay open once it reaches its fully open position. Once this time has elapsed, it will begin hiding.
  • int LeftOffset - The number of pixels the window should be offset from the left side of the desktop. This allows the window to be shifted horizontally, if the default (extreme bottom right corner) is not desired.
  • void Notify() - Tells the window to open.
  • void ForceHidden() - Immediately puts the window into its hiding position.

As mentioned above, TaskbarNotifier should be subclassed in order to add your own content. In the example, the main application window itself is a standard window, not a TaskbarNotifier window. The main window creates and displays the ExampleTaskbarNotifier window in its constructor. At this point it is not visible to the user, because it is too low on the desktop to see.

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

The application specific content of the subclasses TaskbarNotifier window can be defined in XAML like any other WPF window:

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

    <!--<span class="code-comment"> insert your XAML here --></span>

</tn:TaskbarNotifier>

Because the TaskbarNotifier window hides itself completely, it seemed necessary to include some way of opening, configuring, and exiting it. I've included Mariano Omar Rodriguez's NotifyIcon wrapper to allow a system tray icon to be instantiated in XAML. While the demo application does not use the NotifyIcon's balloon tips for anything, it does demonstrate how a system tray icon can be used in conjunction with the Taskbar Notifier.

Here is how the NotifyIcon is declared in the example's XAML:

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

In your application, whenever it makes sense for the TaskbarNotifier to pop up, its Notify() method can be called.

How it Works

The TaskbarNotifier uses a single Storyboard based on the window's Top property, and a DoubleAnimation.

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;

By animating Top, the window's location moves. The size of the window is never changed, as this could potentially change the way the window's contents are organized. The window is simply moved out of view.

In the code, when a change in movement is required the Storyboard is stopped, the To and Duration properties of the DoubleAnimation are altered, and the Storyboard is told to Begin again. For example, if the window is Hiding and the user moves the cursor over the window, the window stops hiding and changes its state to Opening. The Duration is calculated based on the current window location to ensure the desired opening/hiding rate.

The private DisplayState property drives the movement of the window. The possible values of DisplayState are defined in the following enumeration:

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

Whenever DisplayState is altered, the OnDisplayStateChanged method is called:

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

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

OnDisplayStateChanged simply stops the current animation and handles DisplayState based on each of the four possible DisplayStates.

For example, here is how the Storyboard is configured and re-started when the window is told to open:

// 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);

A DispatcherTimer is used to trigger the hiding of the window once it has been in the open state for the duration indicated by StayOpenMilliseconds.

Here is how the DispatcherTimer is created:

// 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);

When the stayOpenTimer elapses, the timer is stopped and as long as the mouse is not over it, the window is told to hide. This is accomplished by setting the DisplayState to 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;
    }
}

For more details, download the source code and check it out.

History

  • December 2007: Initial creation
  • 23 Jan 2008: Quick update to fix some visual problems

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Buzz Weetman
Software Developer
United States United States
software engineer

Comments and Discussions

 
Questionworks fine in VS2010, but if I build it from FinalBuilder ,there is an exception Pinmembernancy200025-Nov-12 23:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 23 Jan 2008
Article Copyright 2008 by Buzz Weetman
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid