(Print this page)

WPF application notifications
Published date: Wednesday, January 13, 2016
On: Moer and Éric Moreau's web site

Sometimes, when building applications, you need to warn users that something just happened but this warning doesn’t always have to be intrusive. In those occasion, a modal message box is not appropriate.

This is exactly what this article is about: showing very simple non-blocking notifications from a WPF application. I will even provide you links to other more complete, fancier and complex components to use (but still free).

Downloadable demo

This month demo solution has been built using Visual Studio 2015 but can be used in older version as well. The demo solution contains both VB and C# projects.

2 different ways!

The projects are showing 2 very easy methods of showing notifications.

The first way is to use the NotifyIcon class provided by the System.Windows.Forms namespace. But surprise, depending on how you pass the values, the components reacts a bit differently. The demo shows both syntax.

The second method is to build yourself your own dialog that will be shown on the bottom right of the screen.

Figure 1: the demo application in action

The first method might be a lot easier but the second one gives you a better effect.

The NotifyIcon class

To be able to use this class, you will need to use System.Windows.Forms and System.Drawing to your references. There is nothing wrong to do it as these 2 references are part of the .Net Framework and already installed on the computers.

As stated in the previous section, the NotifyIcon class offers 2 ways of calling the ShowBalloonTip method. A short signature and a long one. But depending on the overload you take, the result is a bit different.

The following code shows the first overload I want to demonstrate. The important method is the ShowBalloonTip but setting the Icon and the Visible property are mandatory (at least on Windows 10!). The strange thing is that even if we set the Icon property, it won’t be shown when you are using this overload. Instead, the icon shown on the notification is determined by the last parameter to the method (in this case ToolTipIcon.Info showing an exclamation point as shown in figure 2).

Using ni As New NotifyIcon
    ni.Icon = New Icon("Face03.ico")
    ni.Visible = True
    ni.ShowBalloonTip(5000, 
                      "Notification Title", 
                      "Notification Message", 
                      ToolTipIcon.Info)
    ni.Visible = False
End Using

Figure 2: All arguments passed to the ShowBalloonTip method

I said that there is a second overload to the ShowBalloonTip method. Instead of passing the title and the text to show to the method, you can set the values through properties of the component like the following code. Notice that using this syntax, you will see the smiley icon as shown in figure 3 (hopefully you will find a better icon than mine!). So you have a bit more control over what’s shown.

Using ni As New NotifyIcon
    ni.BalloonTipTitle = "Notification Title"
    ni.BalloonTipText = "Notification Message"
    ni.Icon = New Icon("Face03.ico")
    ni.Visible = True
    ni.ShowBalloonTip(5000)
    ni.Visible = False
End Using

Figure 3: The shorter ShowBalloonTip method overload

The choice is really yours depending on which icon you want to show.

Building your own notification

Why limit yourself to this component? Why not build your own? It is not that hard after all.

The first thing you need to do is to build your notification window. You are limited mostly by your imagination. It is a regular window from WPF point of view! My very simple XAML code reads like this:

<Window
    x:Class="NotificationWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Notification Popup" Width="300" SizeToContent="Height"
  WindowStyle="None" AllowsTransparency="True" Background="Transparent">
    <Grid RenderTransformOrigin="0,1" >

        <!-- Notification area -->
        <Border BorderThickness="1" Background="Beige" BorderBrush="Black" CornerRadius="10">
            <StackPanel Margin="20">
                <TextBlock Name="NotificationTitle" TextWrapping="Wrap" FontWeight="Bold" Margin="5">
                    Notification data
                </TextBlock>
                <TextBlock Name="NotificationText" Margin="5" >
                    Something just happened and you are being notified of it.
                </TextBlock>
                <Button Content="Clickable" Margin="5" HorizontalAlignment="Center" Click="ButtonBase_OnClick" />
            </StackPanel>
        </Border>

        <!-- Animation -->
        <Grid.Triggers>
            <EventTrigger RoutedEvent="FrameworkElement.Loaded">
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
                            <SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
                            <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
                        </DoubleAnimationUsingKeyFrames>
                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Completed="DoubleAnimationUsingKeyFrames_Completed">
                            <SplineDoubleKeyFrame KeyTime="0:0:7" Value="1"/>
                            <SplineDoubleKeyFrame KeyTime="0:0:9" Value="0"/>
                        </DoubleAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Grid.Triggers>

        <Grid.RenderTransform>
            <ScaleTransform ScaleY="1" />
        </Grid.RenderTransform>

    </Grid>
</Window>

And the code behind this XAML reads like this:

Option Strict On

Imports System.Windows.Threading

Public Class NotificationWindow

    Public Sub New(ByVal pWindow As Window)

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, 
            New Action(Sub()
                Dim workingArea = Forms.Screen.PrimaryScreen.WorkingArea
                Dim ps = PresentationSource.FromVisual(pWindow)
                If (ps?.CompositionTarget IsNot Nothing) Then
                    Dim t = ps.CompositionTarget.TransformFromDevice
                    Dim corner = t.Transform(New Point(workingArea.Right, workingArea.Bottom))

                    Left = corner.X - ActualWidth - 100
                    Top = corner.Y - ActualHeight
                End If
            End Sub
            ))
    End Sub

    Public Property NotifTitle As String
        Get
            Return NotificationTitle.Text
        End Get
        Set
            NotificationTitle.Text = Value
        End Set
    End Property

    Public Property NotifText As String
        Get
            Return NotificationText.Text
        End Get
        Set
            NotificationText.Text = Value
        End Set
    End Property

    Private Sub DoubleAnimationUsingKeyFrames_Completed(sender As Object, e As EventArgs)
        'To close the window when the animation completes 
        '(otherwise the window is hidden but resources remains loaded)
        close()
    End Sub

    Private Sub ButtonBase_OnClick(sender As Object, e As RoutedEventArgs)
        MessageBox.Show("You clicked the button from the notification!")
    End Sub

End Class

There are a very few interesting things to note about this code.

First, 2 properties let you control the Title and the Text to be shown on your notification (NotifTitle and NotifText).

The XAML creates a button and the code behind handles its click event. Here, a very simple message box is displayed when the button is clicked.

The Completed event of the animation is also handled. Otherwise the window would not be visible after being shown (completely transparent) but resources would still be loaded. It is way better to just close the window.

Last but not least, the constructor of the window handles the displaying of the window and position it in the lower right corner of the main screen.

Once you have it, you can easily show it like this:

Dim nw As New NotificationWindow(Me) With
    {
        .NotifTitle = "Custom title",
        .NotifText = "This is my very own custom text"
    }
nw.Show()

Figure 4: your own notification window

Other components

Other great people have also written about WPF notifications. Here are a few in random order:

Conclusion

There are some times when you want to notify users that something happened (or completed) but don’t want to steal the focus. Notifications are great for that.

Using the second way (your own window), you can go crazy and build a notification that can really be distracting!


(Print this page)