diff --git a/Changelog.md b/Changelog.md index 4fbf7c9..e807daf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,9 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.3.0] - 2018-05-03 +### Added +- Added notification fade-out and fade-in animations. + ## [1.2.0] - 2018-04-30 ### Added - - Implemented support for setting custom text color with `Foreground` method. +- Implemented support for setting custom text color with `Foreground` method. ## [1.1.0] - 2018-04-03 ### Added diff --git a/Enterwell.Clients.Wpf.Notifications.Sample/MainWindow.xaml b/Enterwell.Clients.Wpf.Notifications.Sample/MainWindow.xaml index 2845ecb..be81c4d 100644 --- a/Enterwell.Clients.Wpf.Notifications.Sample/MainWindow.xaml +++ b/Enterwell.Clients.Wpf.Notifications.Sample/MainWindow.xaml @@ -82,7 +82,7 @@ VerticalAlignment="Top" Background="#1751C3" Click="ButtonBaseInfoDelayOnClick" - Content="Info message with delayed dismiss (5s)" + Content="Animated info message with delayed dismiss (5s)" Style="{StaticResource NotificationMessageButtonStyle}" /> diff --git a/Enterwell.Clients.Wpf.Notifications.Sample/MainWindow.xaml.cs b/Enterwell.Clients.Wpf.Notifications.Sample/MainWindow.xaml.cs index 089c783..5a15e12 100644 --- a/Enterwell.Clients.Wpf.Notifications.Sample/MainWindow.xaml.cs +++ b/Enterwell.Clients.Wpf.Notifications.Sample/MainWindow.xaml.cs @@ -18,8 +18,7 @@ public partial class MainWindow /// public MainWindow() { - InitializeComponent(); - + this.InitializeComponent(); this.DataContext = this; } @@ -78,6 +77,9 @@ private void ButtonBaseInfoDelayOnClick(object sender, RoutedEventArgs e) this.Manager .CreateMessage() .Accent("#1751C3") + .Animates(true) + .AnimationInDuration(0.75) + .AnimationOutDuration(2) .Background("#333") .HasBadge("Info") .HasMessage("Update will be installed on next application restart. This message will be dismissed after 5 seconds.") @@ -87,6 +89,12 @@ private void ButtonBaseInfoDelayOnClick(object sender, RoutedEventArgs e) .Queue(); } + /// + /// Gets the notification message manager. + /// + /// + /// The notification message manager. + /// public INotificationMessageManager Manager { get; } = new NotificationMessageManager(); } } diff --git a/Enterwell.Clients.Wpf.Notifications/Controls/NotificationMessage.cs b/Enterwell.Clients.Wpf.Notifications/Controls/NotificationMessage.cs index 8a44d21..85dfba6 100644 --- a/Enterwell.Clients.Wpf.Notifications/Controls/NotificationMessage.cs +++ b/Enterwell.Clients.Wpf.Notifications/Controls/NotificationMessage.cs @@ -3,6 +3,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Media; +using System.Windows.Media.Animation; namespace Enterwell.Clients.Wpf.Notifications.Controls { @@ -11,7 +12,7 @@ namespace Enterwell.Clients.Wpf.Notifications.Controls /// /// /// - public class NotificationMessage : Control, INotificationMessage + public class NotificationMessage : Control, INotificationMessage, INotificationAnimation { /// /// Gets or sets the content of the overlay. @@ -145,6 +146,133 @@ public ObservableCollection Buttons set => SetValue(ButtonsProperty, value); } + /// + /// Gets or sets whether the message animates. + /// + /// + /// Whether or not the message animates. + /// + public bool Animates + { + get => (bool)GetValue(AnimatesProperty); + set => SetValue(AnimatesProperty, value); + } + + /// + /// Gets or sets how long the message animates in (in seconds). + /// + /// + /// How long the message animates in (in seconds). + /// + public double AnimationInDuration + { + get => (double)GetValue(AnimationInDurationProperty); + set => SetValue(AnimationInDurationProperty, value); + } + + /// + /// Gets or sets how long the message animates out (in seconds). + /// + /// + /// How long the message animates out (in seconds). + /// + public double AnimationOutDuration + { + get => (double)GetValue(AnimationOutDurationProperty); + set => SetValue(AnimationOutDurationProperty, value); + } + + /// + /// The animatable element used for show/hide animations. + /// + public UIElement AnimatableElement + { + get => this; + } + + /// + /// The animation in. + /// + public AnimationTimeline AnimationIn + { + get + { + var animation = (AnimationTimeline)GetValue(AnimationInProperty); + if (animation != null) + { + animation.Duration = TimeSpan.FromSeconds(AnimationInDuration); + return animation; + } + else + { + var doubleAnimation = new DoubleAnimation + { + From = 0, + To = 1, + BeginTime = TimeSpan.FromSeconds(0), + Duration = TimeSpan.FromSeconds(AnimationInDuration), + FillBehavior = FillBehavior.HoldEnd + }; + return doubleAnimation; + } + } + set => SetValue(AnimationInProperty, value); + } + + /// + /// The animation out. + /// + public AnimationTimeline AnimationOut + { + get + { + var animation = (AnimationTimeline)GetValue(AnimationOutProperty); + if (animation != null) + { + animation.Duration = TimeSpan.FromSeconds(AnimationOutDuration); + return animation; + } + else + { + return new DoubleAnimation + { + From = 1, + To = 0, + BeginTime = TimeSpan.FromSeconds(0), + Duration = TimeSpan.FromSeconds(AnimationOutDuration), + FillBehavior = FillBehavior.HoldEnd + }; + } + } + set => SetValue(AnimationOutProperty, value); + } + + /// + /// The dependency property on which to animate while animating in. + /// + public DependencyProperty AnimationInDependencyProperty + { + get + { + var property = (DependencyProperty)GetValue(AnimationInDependencyPropProperty); + return property ?? UIElement.OpacityProperty; + } + set => SetValue(AnimationInDependencyPropProperty, value); + } + + /// + /// The dependency property on which to animate while animating out. + /// + public DependencyProperty AnimationOutDependencyProperty + { + get + { + var property = (DependencyProperty)GetValue(AnimationOutDependencyPropProperty); + return property ?? UIElement.OpacityProperty; + } + set => SetValue(AnimationOutDependencyPropProperty, value); + } + /// /// The overlay content property. /// @@ -281,6 +409,48 @@ private static void MessagePropertyChangesCallback(DependencyObject dependencyOb public static readonly DependencyProperty ButtonsProperty = DependencyProperty.Register("Buttons", typeof(ObservableCollection), typeof(NotificationMessage), new PropertyMetadata(null)); + /// + /// The animates property. + /// + public static readonly DependencyProperty AnimatesProperty = + DependencyProperty.Register("Animates", typeof(bool), typeof(NotificationMessage), new PropertyMetadata(false)); + + /// + /// The animation in duration property (in seconds). + /// + public static readonly DependencyProperty AnimationInDurationProperty = + DependencyProperty.Register("AnimationInDuration", typeof(double), typeof(NotificationMessage), new PropertyMetadata(0.25)); + + /// + /// The animation out duration property (in seconds). + /// + public static readonly DependencyProperty AnimationOutDurationProperty = + DependencyProperty.Register("AnimationOutDuration", typeof(double), typeof(NotificationMessage), new PropertyMetadata(0.25)); + + /// + /// The animation in property. + /// + public static readonly DependencyProperty AnimationInProperty = + DependencyProperty.Register("AnimationIn", typeof(AnimationTimeline), typeof(NotificationMessage), new PropertyMetadata(null)); + + /// + /// The animation out property. + /// + public static readonly DependencyProperty AnimationOutProperty = + DependencyProperty.Register("AnimationOut", typeof(AnimationTimeline), typeof(NotificationMessage), new PropertyMetadata(null)); + + /// + /// The animation in dependency property. + /// + public static readonly DependencyProperty AnimationInDependencyPropProperty = + DependencyProperty.Register("AnimationInDependencyProperty", typeof(DependencyProperty), typeof(NotificationMessage), new PropertyMetadata(null)); + + /// + /// The animation out dependency property. + /// + public static readonly DependencyProperty AnimationOutDependencyPropProperty = + DependencyProperty.Register("AnimationOutDependencyProperty", typeof(DependencyProperty), typeof(NotificationMessage), new PropertyMetadata(null)); + /// /// Initializes the class. diff --git a/Enterwell.Clients.Wpf.Notifications/Controls/NotificationMessageContainer.cs b/Enterwell.Clients.Wpf.Notifications/Controls/NotificationMessageContainer.cs index 706181f..9b97e2c 100644 --- a/Enterwell.Clients.Wpf.Notifications/Controls/NotificationMessageContainer.cs +++ b/Enterwell.Clients.Wpf.Notifications/Controls/NotificationMessageContainer.cs @@ -1,6 +1,7 @@ using System; using System.Windows; using System.Windows.Controls; +using System.Windows.Media.Animation; namespace Enterwell.Clients.Wpf.Notifications.Controls { @@ -79,7 +80,30 @@ private void ManagerOnOnMessageDismissed(object sender, NotificationMessageManag throw new InvalidOperationException( "Can't use both ItemsSource and Items collection at the same time."); - this.Items?.Remove(args.Message); + if (args.Message is INotificationAnimation) + { + var animatableMessage = args.Message as INotificationAnimation; + var animation = animatableMessage.AnimationOut; + if (animatableMessage.Animates && animatableMessage.AnimatableElement != null + && animation != null && animatableMessage.AnimationOutDependencyProperty != null) + { + animation.Completed += (s, a) => this.RemoveMessage(args.Message); + animatableMessage.AnimatableElement.BeginAnimation(animatableMessage.AnimationOutDependencyProperty, animation); + } + else + { + this.RemoveMessage(args.Message); + } + } + else + { + this.RemoveMessage(args.Message); + } + } + + private void RemoveMessage(INotificationMessage message) + { + this.Items?.Remove(message); } /// @@ -95,6 +119,17 @@ private void ManagerOnOnMessageQueued(object sender, NotificationMessageManagerE "Can't use both ItemsSource and Items collection at the same time."); this.Items?.Add(args.Message); + + if (args.Message is INotificationAnimation) + { + var animatableMessage = args.Message as INotificationAnimation; + var animation = animatableMessage.AnimationIn; + if (animatableMessage.Animates && animatableMessage.AnimatableElement != null + && animation != null && animatableMessage.AnimationInDependencyProperty != null) + { + animatableMessage.AnimatableElement.BeginAnimation(animatableMessage.AnimationInDependencyProperty, animation); + } + } } /// diff --git a/Enterwell.Clients.Wpf.Notifications/Enterwell.Clients.Wpf.Notifications.csproj b/Enterwell.Clients.Wpf.Notifications/Enterwell.Clients.Wpf.Notifications.csproj index c456114..622097d 100644 --- a/Enterwell.Clients.Wpf.Notifications/Enterwell.Clients.Wpf.Notifications.csproj +++ b/Enterwell.Clients.Wpf.Notifications/Enterwell.Clients.Wpf.Notifications.csproj @@ -58,6 +58,7 @@ + diff --git a/Enterwell.Clients.Wpf.Notifications/Enterwell.Clients.Wpf.Notifications.nuspec b/Enterwell.Clients.Wpf.Notifications/Enterwell.Clients.Wpf.Notifications.nuspec index 61396b7..d7e1a1a 100644 --- a/Enterwell.Clients.Wpf.Notifications/Enterwell.Clients.Wpf.Notifications.nuspec +++ b/Enterwell.Clients.Wpf.Notifications/Enterwell.Clients.Wpf.Notifications.nuspec @@ -10,7 +10,7 @@ https://github.com/Enterwell/Wpf.Notifications false $description$ - Added support for setting custom text color with `Foreground` method. + Added notification fade-out and fade-in animations. Copyright (c) Enterwell d.o.o. 2017-2018 wpf notifications diff --git a/Enterwell.Clients.Wpf.Notifications/INotificationAnimation.cs b/Enterwell.Clients.Wpf.Notifications/INotificationAnimation.cs new file mode 100644 index 0000000..2803df5 --- /dev/null +++ b/Enterwell.Clients.Wpf.Notifications/INotificationAnimation.cs @@ -0,0 +1,54 @@ +using System.Windows; +using System.Windows.Media.Animation; + +namespace Enterwell.Clients.Wpf.Notifications +{ + /// + /// The animation properties for a notification message or some + /// other item. + /// + interface INotificationAnimation + { + /// + /// Gets or sets whether the item animates in and out. + /// + bool Animates { get; set; } + + /// + /// Gets or sets the animation in duration (in seconds). + /// + double AnimationInDuration { get; set; } + + /// + /// Gets or sets the animation out duration (in seconds). + /// + double AnimationOutDuration { get; set; } + + /// + /// Gets or sets the animation in. + /// + AnimationTimeline AnimationIn { get; set; } + + /// + /// Gets or sets the animation out. + /// + AnimationTimeline AnimationOut { get; set; } + + /// + /// Gets or sets the DependencyProperty for the animation in. + /// + DependencyProperty AnimationInDependencyProperty { get; set; } + + /// + /// Gets or sets the DependencyProperty for the animation out. + /// + DependencyProperty AnimationOutDependencyProperty { get; set; } + + /// + /// Gets the animatable UIElement. + /// Typically this is the whole Control object so that the entire + /// item can be animated. + /// + UIElement AnimatableElement { get; } + } +} diff --git a/Enterwell.Clients.Wpf.Notifications/NotificationMessageBuilder.cs b/Enterwell.Clients.Wpf.Notifications/NotificationMessageBuilder.cs index 04dd4fc..d1e449a 100644 --- a/Enterwell.Clients.Wpf.Notifications/NotificationMessageBuilder.cs +++ b/Enterwell.Clients.Wpf.Notifications/NotificationMessageBuilder.cs @@ -1,6 +1,8 @@ using System; using System.Threading.Tasks; +using System.Windows; using System.Windows.Media; +using System.Windows.Media.Animation; namespace Enterwell.Clients.Wpf.Notifications { @@ -109,6 +111,90 @@ public void SetForeground(Brush brush) this.Message.Foreground = brush; } + /// + /// Sets whether or not the message animates. + /// + /// + public void SetAnimates(bool animates) + { + if (this.Message is INotificationAnimation) + { + ((INotificationAnimation)this.Message).Animates = animates; + } + } + + /// + /// Sets the duration for the animation in (in seconds). + /// + /// + public void SetAnimationInDuration(double duration) + { + if (this.Message is INotificationAnimation) + { + ((INotificationAnimation)this.Message).AnimationInDuration = duration; + } + } + + /// + /// Sets the duration for the animation out (in seconds). + /// + /// + public void SetAnimationOutDuration(double duration) + { + if (this.Message is INotificationAnimation) + { + ((INotificationAnimation)this.Message).AnimationOutDuration = duration; + } + } + + /// + /// Sets the animation in for the message. + /// + /// + public void SetAnimationIn(AnimationTimeline animation) + { + if (this.Message is INotificationAnimation) + { + ((INotificationAnimation)this.Message).AnimationIn = animation; + } + } + + /// + /// Sets the animation out for the message. + /// + /// + public void SetAnimationOut(AnimationTimeline animation) + { + if (this.Message is INotificationAnimation) + { + ((INotificationAnimation)this.Message).AnimationOut = animation; + } + } + + /// + /// Sets the animation in dependency property. + /// + /// + public void SetAnimationInDependencyProperty(DependencyProperty property) + { + if (this.Message is INotificationAnimation) + { + ((INotificationAnimation)this.Message).AnimationInDependencyProperty = property; + } + } + + /// + /// Sets the animation out dependency property. + /// + /// + public void SetAnimationOutDependencyProperty(DependencyProperty property) + { + if (this.Message is INotificationAnimation) + { + ((INotificationAnimation)this.Message).AnimationOutDependencyProperty = property; + } + } + /// /// Queues the message to manager. /// diff --git a/Enterwell.Clients.Wpf.Notifications/NotificationMessageBuilderLinq.cs b/Enterwell.Clients.Wpf.Notifications/NotificationMessageBuilderLinq.cs index 1482c73..c4f0e83 100644 --- a/Enterwell.Clients.Wpf.Notifications/NotificationMessageBuilderLinq.cs +++ b/Enterwell.Clients.Wpf.Notifications/NotificationMessageBuilderLinq.cs @@ -1,5 +1,7 @@ using System; +using System.Windows; using System.Windows.Media; +using System.Windows.Media.Animation; namespace Enterwell.Clients.Wpf.Notifications { @@ -282,5 +284,110 @@ public static NotificationMessageBuilder Foreground( return builder; } + + /// + /// Sets whether or not the message animates. + /// + /// The builder. + /// Whether or not the message should animate. + /// + public static NotificationMessageBuilder Animates( + this NotificationMessageBuilder builder, + bool animates) + { + builder.SetAnimates(animates); + + return builder; + } + + /// + /// Sets how long the message animates in (in seconds). + /// + /// The builder. + /// How long the message should animate in (in seconds). + /// + public static NotificationMessageBuilder AnimationInDuration( + this NotificationMessageBuilder builder, + double duration) + { + builder.SetAnimationInDuration(duration); + + return builder; + } + + /// + /// Sets how long the message animates out (in seconds). + /// + /// The builder. + /// How long the message should animate out (in seconds). + /// + public static NotificationMessageBuilder AnimationOutDuration( + this NotificationMessageBuilder builder, + double duration) + { + builder.SetAnimationOutDuration(duration); + + return builder; + } + + /// + /// Sets the animation in for the message. + /// + /// The builder. + /// The message animation in. + /// + public static NotificationMessageBuilder AnimationIn( + this NotificationMessageBuilder builder, + AnimationTimeline animation) + { + builder.SetAnimationIn(animation); + + return builder; + } + + /// + /// Sets the animation out for the message. + /// + /// The builder. + /// The message animation out. + /// + public static NotificationMessageBuilder AnimationOut( + this NotificationMessageBuilder builder, + AnimationTimeline animation) + { + builder.SetAnimationOut(animation); + + return builder; + } + + /// + /// Sets the animation in dependency property for the message. + /// + /// The builder. + /// The animation in dependency property. + /// + public static NotificationMessageBuilder AnimationInDependencyProperty( + this NotificationMessageBuilder builder, + DependencyProperty property) + { + builder.SetAnimationInDependencyProperty(property); + + return builder; + } + + /// + /// Sets the animation out dependency property for the message. + /// + /// The builder. + /// The animation out dependency property. + /// + public static NotificationMessageBuilder AnimationOutDependencyProperty( + this NotificationMessageBuilder builder, + DependencyProperty property) + { + builder.SetAnimationOutDependencyProperty(property); + + return builder; + } } } \ No newline at end of file diff --git a/Enterwell.Clients.Wpf.Notifications/Properties/AssemblyInfo.cs b/Enterwell.Clients.Wpf.Notifications/Properties/AssemblyInfo.cs index 6c8aac3..ba522a6 100644 --- a/Enterwell.Clients.Wpf.Notifications/Properties/AssemblyInfo.cs +++ b/Enterwell.Clients.Wpf.Notifications/Properties/AssemblyInfo.cs @@ -49,5 +49,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.0.0")] -[assembly: AssemblyFileVersion("1.2.0.0")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/Enterwell.Clients.Wpf.Notifications/Themes/Generic.xaml b/Enterwell.Clients.Wpf.Notifications/Themes/Generic.xaml index 81332dd..24c2e1b 100644 --- a/Enterwell.Clients.Wpf.Notifications/Themes/Generic.xaml +++ b/Enterwell.Clients.Wpf.Notifications/Themes/Generic.xaml @@ -185,19 +185,6 @@ - - - - - - + + + + + diff --git a/Readme.md b/Readme.md index 66cae8e..b31aaf8 100644 --- a/Readme.md +++ b/Readme.md @@ -77,6 +77,17 @@ manager.CreateMessage() .Queue(); ``` +### Enable notification message animations (_opt-in feature_) + +```c# +manager + ... + .Animates(true) + .AnimationInDuration(0.75) + .AnimationOutDuration(2) + ... +``` + ## Advanced usage ### Custom control overlay notification