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!