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.
<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.
Width="0" Height="0" WindowStyle="None"
ShowInTaskbar="false" ShowActivated="false"
I'm binding the window position to just above the taskbar using SystemParameters.WorkArea.
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.
Placement="Top"
I'm binding the IsOpen
property of the popup to a property in my view model.
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.
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.
class MyViewModel : INotifyPropertyChanged
{
#region Fields
public event PropertyChangedEventHandler PropertyChanged;
bool _isNotificationOpen = false;
#endregion
#region Properties
public bool IsNotificationOpen
{
get { return _isNotificationOpen; }
set
{
_isNotificationOpen = value;
NotifyPropertyChanged("IsNotificationOpen");
}
}
#endregion
#region Methods
void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
I initialize the view model in the window constructor:
public Window1()
{
MyViewModel viewModel = new MyViewModel();
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.
System.Timers.Timer mytimer = new System.Timers.Timer(2000);
mytimer.Elapsed += new System.Timers.ElapsedEventHandler(delegate
{
viewModel.IsNotificationOpen = !viewModel.IsNotificationOpen;
mytimer.Interval = viewModel.IsNotificationOpen ? 5000 : 2000;
});
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