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

Skinnable and Animated popup window in WPF

, 30 Oct 2011
Rate this:
Please Sign up or sign in to vote.
How to create a skinnable and animatable MSN Live Messenger style popup window in WPF with MVVM design patterns
NotifyMessage Demo

Introduction

The purpose of this article is to demonstrate how to create an animated MSN Live Messenger style popup window in WPF with MVVM design patterns. The popup window employs the fly-in and fade-in animation to move from the Taskbar to the screen area and then fade-out from the screen in 3 seconds.

Background

Traditionally, Windows desktop applications uses Notification Icon in system tray to notify user about some message or status notification. This is a good user experience which doesn't interrupt the user at work while keeping them aware of what's about. However, it renders a poor user experience in a real-time and highly interactive application where it failed to provide multiple or concurrent notification to the user. Personally, I prefer to use the MSN Live Messenger style popup window to solve the above mentioned problem.

Understanding the Code

This demo application is composed of several files, I will focus on the core functionality written in the following files:

  1. NotifyMessageWindow.xaml
  2. NotifyMessageViewModel.cs
  3. NotifyMessageManager.cs

NotifyMessageWindow.xaml

This is a skinnable and animated WPF popup window to show the message notification. To create a skinnable window, we need to set 3 properties of the Window to the following values:

  • AllowsTransparency = "True"
  • Background = "Transparent"
  • WindowsStyle = "None"

Then, we need to create 2 storyboards in the Window.Resources section.

The 'flyinStoryboard' storyboard is responsible for showing the fly-in window by animate the Window.Left and Window.Top properties.

The 'fadeInfadeOutStoryboard' storyboard is responsible for fade-in and fade-out the window by animating the attached property Window.Opacity in 3 seconds.

The data binding {Binding} properties in the resources are coming from the NotifyMessageViewModel view model, which will be revealed later in this article.

 <Window.Resources>
    <Storyboard x:Key="flyinStoryboard">
        <DoubleAnimation From="{Binding Location.FromLeft}" 
        To="{Binding Location.ToLeft}"
            Storyboard.TargetProperty="(Window.Left)" 
            Duration="0:0:1"></DoubleAnimation>
        <DoubleAnimation From="{Binding Location.FromTop}" 
        To="{Binding Location.ToTop}"
            Storyboard.TargetProperty="(Window.Top)" 
            Duration="0:0:1"></DoubleAnimation>
    </Storyboard>
    <Storyboard x:Key="fadeInfadeOutStoryboard">
        <DoubleAnimation Storyboard.TargetProperty="(Window.Opacity)" 
        From="0.0" To="1.0" AutoReverse="True"
            AccelerationRatio="0.4" DecelerationRatio="0.6" 
            Duration="0:0:3"></DoubleAnimation>
    </Storyboard>
</Window.Resources>

Upon Window.Loaded event triggered, we start the 'flyinStoryboard' and 'fadeInfadeOutStoryboard' storyboard together to create a smooth fly-in, then fade-in effect while showing the popup window.

<Window.Triggers>
    <EventTrigger RoutedEvent="Window.Loaded">
        <BeginStoryboard Storyboard="{StaticResource flyinStoryboard}" />
        <BeginStoryboard Storyboard="{StaticResource fadeInfadeOutStoryboard}" />
    </EventTrigger>
</Window.Triggers>

The content layout of the popup window is shown below and it simply binds the SkinName, HeaderText and BodyText properties from the view model.

 <Grid>
    <Grid Margin="5" Background="LightYellow">
        <Grid.Resources>
            <Style x:Key="headerTextStyle" TargetType="TextBlock">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="FontWeight" Value="Bold" />
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="TextTrimming" Value="CharacterEllipsis" />
                <Setter Property="Margin" Value="30 0 5 0" />
            </Style>
            <Style x:Key="bodyTextStyle" TargetType="TextBlock">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="VerticalAlignment" Value="Top" />
                <Setter Property="TextWrapping" Value="Wrap" />
                <Setter Property="TextTrimming" Value="CharacterEllipsis" />
                <Setter Property="Margin" Value="10 0 10 0" />
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="25" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Style="{StaticResource headerTextStyle}" 
        Text="{Binding Message.HeaderText}"></TextBlock>
        <TextBlock Grid.Row="1" Style="{StaticResource bodyTextStyle}" 
        Text="{Binding Message.BodyText}"></TextBlock>
    </Grid>
    <Image Source="{Binding Message.SkinName}" Stretch="Fill" />
</Grid>

Popup Window Content Layout The content layout of the Markup code shown above.

In this demo project, I've created 2 skin images named as 'BlueSkin.png' and 'GreenSkin.png', respectively. You will get to know how to use them in the section titled 'Using the Code' later in this article.

Blue Skin Green Skin

NotifyMessageViewModel.cs

This class serves as the view model to the popup window. It contains the following properties:

  • Message - It contains the SkinName, HeaderText and BodyText for content display.
  • Location - It instructs the storyboard where to show the popup window in the screen location.
  • ClickCommand - Action to be taken when popup window is being clicked.
  • CloseCommand - Action to be taken while popup window is closed.

The properties shown above are pretty self-explanatory. User can customize the ClickCommand as of what to do when a popup window is being clicked. For example, showing the detailed message, navigate to another view, open the Internet Explorer and so on. The CloseCommand is used to notify NotifyMessageManager when the popup window is closed.

NotifyMessageManager.cs

This class is responsible for storing and displaying the queued messages. In order to support highly concurrent scenario, it employs a ConcurrentQueue data structure internally to store and retrieve the queued messages in First-In-First-Out (FIFO) manner. Its primary job is to start a background Task to display the popup window as soon as a message is enqueued in the system and stop while there is no message in the queue.

 var dispatcher = Application.Current.MainWindow.Dispatcher;

return Task.Factory.StartNew(() => {
    do
    {
        // Gets the next available display location in the screen
        int nextLocation = FindNextLocation();

        if (nextLocation > -1)
        {
            NotifyMessage msg = null;
            // Retrieve the message from the queue
            if (QueuedMessages.TryDequeue(out msg))
            {
                // construct a View Model and bind it to the Popup Window
                var viewModel = new NotifyMessageViewModel(msg, 
                    DisplayLocations[nextLocation], 
                    () => DisplayMessages[nextLocation] = 
                    null); // Free the display location once the popup window is closed
                DisplayMessages[nextLocation] = viewModel;

                // Use Application.Current.MainWindow.Dispatcher to 
                // switch back to the UI Thread to create popup window
                dispatcher.BeginInvoke(
                    new MethodInvoker(() => 
                        {
                            var window = new NotifyMessageWindow() 
                                { 
                                    Owner = Application.Current.MainWindow,
                                    DataContext = viewModel,
                                    ShowInTaskbar = false
                                };
                            window.Show();
                        }), DispatcherPriority.Background);
            }
        }
        Thread.Sleep(1000);
    } while (QueuedMessages.Count > 0 && !cancellationToken.IsCancellationRequested);
    Stop();
});

Using the Code

First of all, create a singleton NotifyMessageManager in your application. You need to provide 4 required parameters to the constructor as shown in the code below. The popup window's width and height must be the same as the Skin's width and height. In our case, we are using 200x150 pixels skin here.

var notifyMessageMgr = new NotifyMessageManager(
			Screen.Width,	// Screen width
			Screen.Height,	// Screen height excludes the Taskbar height
			200,		// Popup Window width
			150		// Popup Window height
);

Next, create one instance of NotifyMessage and enqueue it to the NotifyMessageManager as shown in the code below:

var msg = new NotifyMessage(
        "Images/BlueSkin.png",		// Skin file name - Choose your favorite skin
        "Blue Skin Title",			// The message title
        "Blue Skin has been chosen.",	// The message content
        () => MessageBox.Show("Blue Skin has been chosen.")	// Show a MessageBox 
					// when user clicks the popup window
);
notifyMessageMgr.EnqueueMessage(msg);

You will see a popup window show up in the bottom left of the screen after the message is enqueued. Last, but not least, I've included a sample, available from the link at the top of this article, which demonstrates what I've described in this article.

Conclusion

To run this demo on your computer, you need to download the Microsoft Expression Blend Software Development Kit (SDK) for .NET 4 or manage to get the following library files:

  • Microsoft.Expression.Interactions.dll
  • System.Windows.Interactivity.dll

History

  • 29th October, 2011: Initial version

License

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

Share

About the Author

Elvin Cheng

Singapore Singapore
Elvin Cheng is currently living in Woodlands, Singapore. He has been developing applications with the .NET Framework, using C# and ASP.NET since October 2002. Elvin specializes in building Real-time monitoring and tracking information system for Semi-conductor manufacturing industry. During his spare time, he enjoys reading books, watching movie and gym.
Follow on   Twitter

Comments and Discussions

 
Questionpop up notification on framework 3.5 version PinmemberMember 1089200319-Jun-14 2:32 
QuestionGOOD WORK Pinmemberahug91z3-Dec-13 20:23 
GeneralBrilliant article Pinmemberguntupalli_hanu22-Jul-13 10:03 
GeneralMy vote of 5 Pinmembervicentehg29-May-13 10:39 
Questionwindow is minimized PinmemberMember 975142523-Feb-13 11:35 
GeneralMy vote of 5 PinmemberSimon Ayoub13-Dec-12 15:47 
GeneralMy vote of 5 PinmemberRob10310-Apr-12 15:38 
GeneralMy vote of 5 Pinmemberarisoqq5-Dec-11 5:05 
Question[My vote of 2] Very good attempt PinmemberSARMAHARI7-Nov-11 13:43 
GeneralMy vote of 4 Pinmemberr_hyde1-Nov-11 13:00 

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
Web03 | 2.8.140821.2 | Last Updated 30 Oct 2011
Article Copyright 2011 by Elvin Cheng
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid