Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stacked in-app notification #1921

Merged
merged 9 commits into from
Apr 16, 2018
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,15 @@
ShowDismissButton="@[ShowDismissButton:Bool:True]"
AnimationDuration="@[AnimationDuration:TimeSpan:100:0-5000]"
VerticalOffset="@[VerticalOffset:DoubleSlider:100.0:-200.0-200.0]"
HorizontalOffset="@[HorizontalOffset:DoubleSlider:0.0:-200.0-200.0]" />
HorizontalOffset="@[HorizontalOffset:DoubleSlider:0.0:-200.0-200.0]"
StackMode="@[StackMode:Enum:StackMode.Replace]" />

<controls:InAppNotification x:Name="ExampleVSCodeInAppNotification"
Style="{StaticResource VSCodeNotificationStyle}"
AnimationDuration="@[AnimationDuration]"
VerticalOffset="@[VerticalOffset]"
HorizontalOffset="@[HorizontalOffset]">
HorizontalOffset="@[HorizontalOffset]"
StackMode="@[StackMode]">
<controls:InAppNotification.ContentTemplate>
<DataTemplate>
<Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public partial class InAppNotification
public static readonly DependencyProperty HorizontalOffsetProperty =
DependencyProperty.Register(nameof(HorizontalOffset), typeof(double), typeof(InAppNotification), new PropertyMetadata(0));

/// <summary>
/// Identifies the <see cref="StackMode"/> dependency property.
/// </summary>
public static readonly DependencyProperty StackModeProperty =
DependencyProperty.Register(nameof(StackMode), typeof(StackMode), typeof(InAppNotification), new PropertyMetadata(StackMode.Replace));

/// <summary>
/// Gets or sets a value indicating whether to show the Dismiss button of the control.
/// </summary>
Expand Down Expand Up @@ -80,6 +86,15 @@ public double HorizontalOffset
set { SetValue(HorizontalOffsetProperty, value); }
}

/// <summary>
/// Gets or sets a value indicating the stack mode of the notifications.
/// </summary>
public StackMode StackMode
{
get { return (StackMode)GetValue(StackModeProperty); }
set { SetValue(StackModeProperty, value); }
}

private static void OnShowDismissButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var inApNotification = d as InAppNotification;
Expand Down
118 changes: 108 additions & 10 deletions Microsoft.Toolkit.Uwp.UI.Controls/InAppNotification/InAppNotification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
// ******************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;

namespace Microsoft.Toolkit.Uwp.UI.Controls
{
Expand All @@ -30,6 +31,7 @@ public sealed partial class InAppNotification : ContentControl
private DispatcherTimer _dismissTimer = new DispatcherTimer();
private Button _dismissButton;
private VisualStateGroup _visualStateGroup;
private List<NotificationOptions> _stackedNotificationOptions = new List<NotificationOptions>();

/// <summary>
/// Initializes a new instance of the <see cref="InAppNotification"/> class.
Expand Down Expand Up @@ -108,9 +110,12 @@ public void Show(int duration = 0)
/// <param name="duration">Displayed duration of the notification in ms (less or equal 0 means infinite duration)</param>
public void Show(string text, int duration = 0)
{
ContentTemplate = null;
Content = text;
Show(duration);
var notificationOptions = new NotificationOptions
{
Duration = duration,
Content = text
};
Show(notificationOptions);
}

/// <summary>
Expand All @@ -120,9 +125,12 @@ public void Show(string text, int duration = 0)
/// <param name="duration">Displayed duration of the notification in ms (less or equal 0 means infinite duration)</param>
public void Show(UIElement element, int duration = 0)
{
ContentTemplate = null;
Content = element;
Show(duration);
var notificationOptions = new NotificationOptions
{
Duration = duration,
Content = element
};
Show(notificationOptions);
}

/// <summary>
Expand All @@ -132,9 +140,12 @@ public void Show(UIElement element, int duration = 0)
/// <param name="duration">Displayed duration of the notification in ms (less or equal 0 means infinite duration)</param>
public void Show(DataTemplate dataTemplate, int duration = 0)
{
ContentTemplate = dataTemplate;
Content = null;
Show(duration);
var notificationOptions = new NotificationOptions
{
Duration = duration,
Content = dataTemplate
};
Show(notificationOptions);
}

/// <summary>
Expand All @@ -153,6 +164,18 @@ private void Dismiss(InAppNotificationDismissKind dismissKind)
{
if (Visibility == Visibility.Visible)
{
// Continue to display notification if on remaining stacked notification
if (_stackedNotificationOptions.Any())
{
_stackedNotificationOptions.RemoveAt(0);

if (_stackedNotificationOptions.Any())
{
DisplayNextStackedNotification(_stackedNotificationOptions[0]);
return;
}
}

_animationTimer.Stop();

var closingEventArgs = new InAppNotificationClosingEventArgs(dismissKind);
Expand All @@ -172,5 +195,80 @@ private void Dismiss(InAppNotificationDismissKind dismissKind)
_animationTimer.Start();
}
}

/// <summary>
/// Informs if the notification should be displayed immediately (based on the StackMode)
/// </summary>
/// <returns>True if notification should be displayed immediately</returns>
private bool ShouldDisplayImmediately()
{
return StackMode != StackMode.QueueBehind ||
(StackMode == StackMode.QueueBehind && _stackedNotificationOptions.Count == 0);
}

/// <summary>
/// Display the next stacked notification using StackedNotificationInfo
/// </summary>
/// <param name="notificationOptions">Information to display for the next notification</param>
private void DisplayNextStackedNotification(NotificationOptions notificationOptions)
{
UpdateContent(notificationOptions);

_dismissTimer.Stop();

if (notificationOptions.Duration > 0)
{
_dismissTimer.Interval = TimeSpan.FromMilliseconds(notificationOptions.Duration);
_dismissTimer.Start();
}
}

/// <summary>
/// Update the Content of the notification
/// </summary>
/// <param name="notificationOptions">Information about the notification to display</param>
private void UpdateContent(NotificationOptions notificationOptions)
{
switch (notificationOptions.Content)
{
case string text:
ContentTemplate = null;
Content = text;
break;
case UIElement element:
ContentTemplate = null;
Content = element;
break;
case DataTemplate dataTemplate:
ContentTemplate = dataTemplate;
Content = null;
break;
}
}

/// <summary>
/// Handle the display of the notification based on the current StackMode
/// </summary>
/// <param name="notificationOptions">Information about the notification to display</param>
private void Show(NotificationOptions notificationOptions)
{
bool shouldDisplayImmediately = ShouldDisplayImmediately();

if (StackMode == StackMode.QueueBehind)
{
_stackedNotificationOptions.Add(notificationOptions);
}

if (StackMode == StackMode.StackInFront)
{
_stackedNotificationOptions.Insert(0, notificationOptions);
}

if (shouldDisplayImmediately)
{
UpdateContent(notificationOptions);
Show(notificationOptions.Duration);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// ******************************************************************
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
// ******************************************************************

using Windows.UI.Xaml;

namespace Microsoft.Toolkit.Uwp.UI.Controls
{
/// <summary>
/// Base class that contains options of notification
/// </summary>
internal class NotificationOptions
{
/// <summary>
/// Gets or sets duration of the stacked notification
/// </summary>
public int Duration { get; set; }

/// <summary>
/// Gets or sets Content of the notification
/// Could be either a <see cref="string"/> or a <see cref="UIElement"/> or a <see cref="DataTemplate"/>
/// </summary>
public object Content { get; set; }
}
}
35 changes: 35 additions & 0 deletions Microsoft.Toolkit.Uwp.UI.Controls/InAppNotification/StackMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// ******************************************************************
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
// ******************************************************************

namespace Microsoft.Toolkit.Uwp.UI.Controls
{
/// <summary>
/// The Stack mode of an in-app notification.
/// </summary>
public enum StackMode
{
/// <summary>
/// Each notification will replace the previous one
/// </summary>
Replace,

/// <summary>
/// Opening a notification will display it immediately, remaining notifications will appear when a notification is dismissed
/// </summary>
StackInFront,

/// <summary>
/// Dismissing a notification will show the next one in the queue
/// </summary>
QueueBehind
}
}
11 changes: 11 additions & 0 deletions docs/controls/InAppNotification.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ ExampleInAppNotification.Dismiss();
If you want to fully customize the in-app notification, you will see that the Dismiss button is still visible.
To hide it, simply set the property to `ShowDismissButton="False"`.

### StackMode

By default, each time you display an in-app notification using the same control, each notification will replace the previous one.
You can change this behavior with one of these values:

| StackMode properties | Description |
| -- | -- |
| `Replace` | Default mode, replace previous notification |
| `QueueBehind` | Store every notifications to show, when you dismiss a notification the remaining ones will be displayed successively |
| `StackInFront` | Store every notifications to show, when you show a notification it will be displayed in priority (in the reverse order of `QueueBehind` mode) |

## Events

### Opening
Expand Down