Click here to Skip to main content
Click here to Skip to main content

WPF Taskbar Notifier - A WPF Taskbar Notification Window

By , 22 Jan 2008
 
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"
    >

    <!-- insert your XAML here -->

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

About the Author

Buzz Weetman
Software Developer
United States United States
Member
software engineer

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: Not working with VS2008 sp1 on Vista sp1?memberSerge Calderara17 Apr '09 - 0:04 
Hello,
 
I have test this notification window as well under VIsta and VS2008. For that I have integarted in my own application and all is in the same assembly. I have change the name space according to my application and when I make simple test to call for instand the windo to open at startup, nothing happen. Here is how I have implemented :
 
<tn:StateNotifier x:Class="Maillefer.Nomos.Plateform.UI.StateNotifierWindow"
xmlns:tn="clr-namespace:Maillefer.Nomos.Plateform.UI"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="160" Width="300"
x:Name="StateControl"
 
StateNotifierWindow is just renamed from original sample and StateNotifier too. So the class and the Window is define in the same assembly "Maillefer.Nomos.Plateform.UI"
 
Is tehre something to do to make in working in same assembly or do I have to separate it too as in the sample ??
 
Thanks for help
Regards
______________________________
Serge Calderara
OWner - .Net Consultnat
"Your own experience is build from the one of others"

AnswerRe: Not working with VS2008 sp1 on Vista sp1?memberBuzz Weetman26 Dec '08 - 4:38 
Sorry... I just don't have VS2008 to work with.
I might be able to get it sometime in the next year.
 
If you make any changes that fix the problem, let me know.
GeneralRe: Not working with VS2008 sp1 on Vista sp1? [modified]memberPhilipp Sumi2 Apr '09 - 20:36 
I've been working on a pure-WPF solution (VS 2008, .NET 3.5 only, however) that provides similar tooltips. Attention: Shameless self-advertisment Wink | ;)
 
Edit: V1.0 release:
http://www.hardcodet.net/wpf-notifyicon[^]
 
...The sample application provides a sliding balloon message that pretty much provides a comparable animation as Buzz' solution.
 
Cheers,
Philipp
 
code hard @ hardcodet.net
modified on Sunday, May 10, 2009 5:45 AM

GeneralHi You!memberDoan Van Tuan30 Oct '08 - 22:06 
I am very happy with this sample, but i was having a issue is that: ContextMenu poped-up can not auto-close when mouse moving out of its scope as classic contextmenu(ContextMenuTrip) can do.
Can you help me resolve this probem?
Thanks
DX
GeneralAnnoying Flickermemberroachslayer7 Aug '08 - 23:16 
Love the example. I did notice a flicker though. Any ideas on eliminating that? I suspect its mostly due to the fact that the animation is traveling over the system tray and Windows (Vista in my case) is trying to refresh that bottom right corner.
 
Isnt there a way to animate it coming out of the top of the taskbar/system tray? rather than from the very bottom of the screen?
GeneralRe: Annoying FlickermemberBuzz Weetman8 Aug '08 - 3:01 
I'm not sure how to remove the flicker... and unfortunately I just don't have the time to really get into right now.
 
The window is never actually changing size. It is just moving.
So when it moves under the taskbar/system tray it is also moving off the bottom of the screen.
 
I thought about animating the size of the window instead, but then there are other issues. Like if the window had a border. The bottom border would need to disappear or it would look strange.
And changing the size of a window would mess up the content within the window in many/most cases. I'm not sure how I'd avoid that.
 
Doesn't WPF offer some kind of cool 2d/3d rendering that would make this even cooler. I need to find the time to play with this kind of stuff.
 
If you feel like working on it, I'd love to hear about your findings.
Buzz
GeneralRe: Annoying Flickermemberroachslayer8 Aug '08 - 6:47 
There are improvements coming in .NET 3.5 SP1 for 2D and 3D animation perf, etc. But I don't think that is the prob here on this basic animation anyway. There are other techniques that might work better as far as the animation goes. I will be happy to send you any updates I make.
QuestionVery nice butmemberBenoît Dion14 Mar '08 - 4:56 
Nice article and very helpful but if the Notify() method is called while another window is shown using ShowDialog(), we cannot interact with the TaskNotifier window.
 
Any workaround?
GeneralRe: Very nice butmemberBuzz Weetman14 Mar '08 - 6:12 
Very nice butt? Oh... nevermind. Laugh | :laugh:
 
I don't think I understand the situation you are describing.
I tried to recreate your problem using the example.
I added a "Test" button to the window that pops up when "Configure..." is chosen from the tray icon's context menu.
When I click the Test button, I create a simple window "TestWindow" and ShowDialog().
Of course, since I did a ShowDialog(), I can't bring the configure window back into focus until I close the modal TestWindow.
On the TestWindow I have a "Notify" button, which when clicked calls Notify() on the taskbarNotifier (which I passed in to the constructor of the TestWindow).
 
The TaskbarNotifier pops up. No problem.
I can click on controls within it. No problem.
The tray icon context menu works. No problem.
 
ShowDialog() blocks, so you can't interact with the window that it was called from. But that is expected... right?
 
Can you call Show() instead?
 
If you can give me more details I will try to help more.
Buzz
GeneralRe: Very nice butmemberBenoît Dion14 Mar '08 - 6:36 
It indeed works as expected in your project.
 
But not in mine Laugh | :laugh: . I ll dig deeper into it this week-end.
 
And yes, if i call Show() instead, I can click controls inside the taskbarnotifier. But I need a modal behaviour.
 
Thanks for your help

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

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