Click here to Skip to main content
15,884,739 members
Articles / Desktop Programming / WPF
Tip/Trick

MVVM Taskbar Notifier

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
20 Jun 2014CPOL2 min read 14.8K   291   12  
Implements a taskbar notifier using an invisible window and a popup control

Introduction

This tip demonstrates a quick solution for a popup taskbar notifier (similar to antivirus update) that does not involve any custom storyboards/animations and does interact with any visuals in the code behind. The solution I came up with is sort of a cheat; It leverages the built in popup control to perform the notification.

Background

I wanted to develop a simple WPF application to popup in the corner of the screen. I also wanted to maintain the MVVM design pattern. I came across two solutions already on CodeProject.

The first article can be found here:

This involves animating the top property of the window. Although it works, the code involves placing storyboards and animations in the code behind and animating visual properties. This is a violation of the MVVM design principle and also the animation is not quite as smooth as I would like. I have come to realise this is because when you animate a window object, some part of that window is not in the WPF sandbox. It is part WPF and part Native. More information can be found here:

The second article I came across can be found here:

Again, the solution works fine but again it involves controlling animation from the code behind.

Please note that both of the above solutions combine with some form of notifyIcon control which provides extra functionality. This type of control could also compliment my solution if desired.

Using the Code

The XAML is straightforward enough.

XML
<Window x:Class="TaskBarNotifier.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="0" Height="0" WindowStyle="None" 
    ShowInTaskbar="false" ShowActivated="false"
    Top="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Bottom}"
    Left="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Right}">
    <Grid>
        <Popup DataContext="{Binding RelativeSource={RelativeSource FindAncestor, 
        AncestorType={x:Type Window}}, Path=DataContext}" VerticalOffset="-8"
          Placement="Top" IsOpen="{Binding IsNotificationOpen}" 
          AllowsTransparency="True" PopupAnimation="Slide" StaysOpen="False">
            <Border BorderBrush="Black" BorderThickness="1" Background="White">
                <Grid Height="150" Width="500">
                    <Viewbox HorizontalAlignment="Left">
                        <Label Content="Your content"/>
                    </Viewbox>
                </Grid>
            </Border>
        </Popup>
    </Grid>
</Window>

Note I am configuring the window itself to be invisible.

XML
Width="0" Height="0" WindowStyle="None" 
ShowInTaskbar="false" ShowActivated="false"

I'm binding the window position to just above the taskbar using SystemParameters.WorkArea.

XML
Top="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Bottom}"
    Left="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Right}"

The next thing of interest is that I am setting the popup to attach to the top of the window. See popup placement behaviour for more information.

XML
Placement="Top"

I'm binding the IsOpen property of the popup to a property in my view model.

XML
IsOpen="{Binding IsNotificationOpen}"

and I'm selecting to use the built in slide popup animation. Please note you could change the animation to any of the PopupAnimation enumeration values.

XML
AllowsTransparency="True" PopupAnimation="Slide"

That's it for the XAML. You can place your content in the popup container and it will slide into view whenever the IsNotificationProperty in the view model changes to true.

Speaking of view model, here it is.

C#
class MyViewModel : INotifyPropertyChanged
    {
        #region Fields

        /// <summary>
        /// PropertyChanged event of type PropertyChangedEventHandler
        /// delegate. Event is raised when a property is changed on
        /// a component.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Indicates whether or not the popup
        /// notification is being displayed.
        /// </summary>
        bool _isNotificationOpen = false;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets whether the notification popup is open.
        /// </summary>
        public bool IsNotificationOpen
        {
            get { return _isNotificationOpen; }
            set
            {
                //Store notification.
                _isNotificationOpen = value;

                //Send notification to binding targets.
                NotifyPropertyChanged("IsNotificationOpen");
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Checks that there is atleast one listener
        /// attached before firing the PropertyChanged Event.
        /// </summary>
        /// <param name="info">The name of the property that has changed.</param>
        void NotifyPropertyChanged(String info)
        {
            //If the event is not nothing.
            if (PropertyChanged != null)
            {
                //Fire event.
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #endregion
    }

I initialize the view model in the window constructor:

C#
/// <summary>
        /// Default constructor.
        /// </summary>
        public Window1()
        {
 
//Initialize the global view model instance.
            MyViewModel viewModel = new MyViewModel();

            //Set the datacontext of this window to the view model.
            DataContext = viewModel;

I've also setup a simple timer in the constructor to toggle the IsNotificationOpen property so you can see the behaviour. Obviously, this timer would be removed in a real project.

C#
//Initialize timer for button held events.
           System.Timers.Timer mytimer = new System.Timers.Timer(2000);

           //Close and Open each tick.
           mytimer.Elapsed += new System.Timers.ElapsedEventHandler(delegate
           {
               //Update the view model to open/close the popup.
               viewModel.IsNotificationOpen = !viewModel.IsNotificationOpen;

               //Change the interval so it stays open longer.
               mytimer.Interval = viewModel.IsNotificationOpen ? 5000 : 2000;

           });

           //Start the timer.
           mytimer.Start();

Points of Interest

Please note that using the built in animation types, you will not have control over any aspect of the animation, i.e., you cannot slow it down or speed it up.

History

  • Version 1.0

License

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


Written By
Ireland Ireland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --