committed by
GitHub
32 changed files with 953 additions and 7 deletions
@ -1 +1 @@ |
|||
Subproject commit aef10ae67dc55c95f49b52a505a0be33bfa297a5 |
|||
Subproject commit 4464343aef5c8ab7a42fcb20a483a6058199f8b8 |
|||
@ -0,0 +1,10 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="ControlCatalog.Pages.NotificationsPage"> |
|||
<StackPanel Orientation="Vertical" Spacing="4" HorizontalAlignment="Left"> |
|||
<TextBlock Classes="h1">Notifications</TextBlock> |
|||
<Button Content="Show Standard Managed Notification" Command="{Binding ShowManagedNotificationCommand}" /> |
|||
<Button Content="Show Custom Managed Notification" Command="{Binding ShowCustomManagedNotificationCommand}" /> |
|||
<Button Content="Show Native Notification" Command="{Binding ShowNativeNotificationCommand}" /> |
|||
</StackPanel> |
|||
</UserControl> |
|||
@ -0,0 +1,18 @@ |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace ControlCatalog.Pages |
|||
{ |
|||
public class NotificationsPage : UserControl |
|||
{ |
|||
public NotificationsPage() |
|||
{ |
|||
this.InitializeComponent(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using System.Reactive; |
|||
using Avalonia.Controls.Notifications; |
|||
using Avalonia.Diagnostics.ViewModels; |
|||
using ReactiveUI; |
|||
|
|||
namespace ControlCatalog.ViewModels |
|||
{ |
|||
class MainWindowViewModel : ViewModelBase |
|||
{ |
|||
private IManagedNotificationManager _notificationManager; |
|||
|
|||
public MainWindowViewModel(IManagedNotificationManager notificationManager) |
|||
{ |
|||
_notificationManager = notificationManager; |
|||
|
|||
ShowCustomManagedNotificationCommand = ReactiveCommand.Create(() => |
|||
{ |
|||
NotificationManager.Show(new NotificationViewModel(NotificationManager) { Title = "Hey There!", Message = "Did you know that Avalonia now supports Custom In-Window Notifications?" }); |
|||
}); |
|||
|
|||
ShowManagedNotificationCommand = ReactiveCommand.Create(() => |
|||
{ |
|||
NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Welcome", "Avalonia now supports Notifications.", NotificationType.Information)); |
|||
}); |
|||
|
|||
ShowNativeNotificationCommand = ReactiveCommand.Create(() => |
|||
{ |
|||
NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Error", "Native Notifications are not quite ready. Coming soon.", NotificationType.Error)); |
|||
}); |
|||
} |
|||
|
|||
public IManagedNotificationManager NotificationManager |
|||
{ |
|||
get { return _notificationManager; } |
|||
set { this.RaiseAndSetIfChanged(ref _notificationManager, value); } |
|||
} |
|||
|
|||
public ReactiveCommand<Unit, Unit> ShowCustomManagedNotificationCommand { get; } |
|||
|
|||
public ReactiveCommand<Unit, Unit> ShowManagedNotificationCommand { get; } |
|||
|
|||
public ReactiveCommand<Unit, Unit> ShowNativeNotificationCommand { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using System.Reactive; |
|||
using Avalonia.Controls.Notifications; |
|||
using ReactiveUI; |
|||
|
|||
namespace ControlCatalog.ViewModels |
|||
{ |
|||
public class NotificationViewModel |
|||
{ |
|||
public NotificationViewModel(INotificationManager manager) |
|||
{ |
|||
YesCommand = ReactiveCommand.Create(() => |
|||
{ |
|||
manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today.")); |
|||
}); |
|||
|
|||
NoCommand = ReactiveCommand.Create(() => |
|||
{ |
|||
manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today. To find out more visit...")); |
|||
}); |
|||
} |
|||
|
|||
public string Title { get; set; } |
|||
public string Message { get; set; } |
|||
|
|||
public ReactiveCommand<Unit, Unit> YesCommand { get; } |
|||
|
|||
public ReactiveCommand<Unit, Unit> NoCommand { get; } |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="ControlCatalog.Views.CustomNotificationView"> |
|||
<Border Padding="12" MinHeight="20" Background="DodgerBlue"> |
|||
<Grid ColumnDefinitions="Auto,*"> |
|||
<Panel Margin="0,0,12,0" Width="25" Height="25" VerticalAlignment="Top"> |
|||
<TextBlock Text="" FontFamily="Segoe UI Symbol" FontSize="20" TextAlignment="Center" VerticalAlignment="Center"/> |
|||
</Panel> |
|||
<DockPanel Grid.Column="1"> |
|||
<TextBlock DockPanel.Dock="Top" Text="{Binding Title}" FontWeight="Medium" /> |
|||
<StackPanel Spacing="20" DockPanel.Dock="Bottom" Margin="0,8,0,0" Orientation="Horizontal"> |
|||
<Button Content="No" DockPanel.Dock="Right" NotificationCard.CloseOnClick="True" Command="{Binding NoCommand}" Margin="0,0,8,0" /> |
|||
<Button Content="Yes" DockPanel.Dock="Right" NotificationCard.CloseOnClick="True" Command="{Binding YesCommand}" /> |
|||
</StackPanel> |
|||
<TextBlock Text="{Binding Message}" TextWrapping="Wrap" Opacity=".8" Margin="0,8,0,0"/> |
|||
</DockPanel> |
|||
</Grid> |
|||
</Border> |
|||
</UserControl> |
|||
@ -0,0 +1,18 @@ |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace ControlCatalog.Views |
|||
{ |
|||
public class CustomNotificationView : UserControl |
|||
{ |
|||
public CustomNotificationView() |
|||
{ |
|||
this.InitializeComponent(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a notification manager that can show arbitrary content.
|
|||
/// Managed notification managers can show any content.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Because notification managers of this type are implemented purely in managed code, they
|
|||
/// can display arbitrary content, as opposed to notification managers which display notifications
|
|||
/// using the host operating system's notification mechanism.
|
|||
/// </remarks>
|
|||
public interface IManagedNotificationManager : INotificationManager |
|||
{ |
|||
/// <summary>
|
|||
/// Shows a notification.
|
|||
/// </summary>
|
|||
/// <param name="content">The content to be displayed.</param>
|
|||
void Show(object content); |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a notification that can be shown in a window or by the host operating system.
|
|||
/// </summary>
|
|||
public interface INotification |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the Title of the notification.
|
|||
/// </summary>
|
|||
string Title { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the notification message.
|
|||
/// </summary>
|
|||
string Message { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="NotificationType"/> of the notification.
|
|||
/// </summary>
|
|||
NotificationType Type { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the expiration time of the notification after which it will automatically close.
|
|||
/// If the value is <see cref="TimeSpan.Zero"/> then the notification will remain open until the user closes it.
|
|||
/// </summary>
|
|||
TimeSpan Expiration { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets an Action to be run when the notification is clicked.
|
|||
/// </summary>
|
|||
Action OnClick { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets an Action to be run when the notification is closed.
|
|||
/// </summary>
|
|||
Action OnClose { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a notification manager that can be used to show notifications in a window or using
|
|||
/// the host operating system.
|
|||
/// </summary>
|
|||
public interface INotificationManager |
|||
{ |
|||
/// <summary>
|
|||
/// Show a notification.
|
|||
/// </summary>
|
|||
/// <param name="notification">The notification to be displayed.</param>
|
|||
void Show(INotification notification); |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
/// <summary>
|
|||
/// A notification that can be shown in a window or by the host operating system.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This class represents a notification that can be displayed either in a window using
|
|||
/// <see cref="WindowNotificationManager"/> or by the host operating system (to be implemented).
|
|||
/// </remarks>
|
|||
public class Notification : INotification |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Notification"/> class.
|
|||
/// </summary>
|
|||
/// <param name="title">The title of the notification.</param>
|
|||
/// <param name="message">The message to be displayed in the notification.</param>
|
|||
/// <param name="type">The <see cref="NotificationType"/> of the notification.</param>
|
|||
/// <param name="expiration">The expiry time at which the notification will close.
|
|||
/// Use <see cref="TimeSpan.Zero"/> for notifications that will remain open.</param>
|
|||
/// <param name="onClick">An Action to call when the notification is clicked.</param>
|
|||
/// <param name="onClose">An Action to call when the notification is closed.</param>
|
|||
public Notification(string title, |
|||
string message, |
|||
NotificationType type = NotificationType.Information, |
|||
TimeSpan? expiration = null, |
|||
Action onClick = null, |
|||
Action onClose = null) |
|||
{ |
|||
Title = title; |
|||
Message = message; |
|||
Type = type; |
|||
Expiration = expiration.HasValue ? expiration.Value : TimeSpan.FromSeconds(5); |
|||
OnClick = onClick; |
|||
OnClose = onClose; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public string Title { get; private set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public string Message { get; private set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public NotificationType Type { get; private set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public TimeSpan Expiration { get; private set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public Action OnClick { get; private set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public Action OnClose { get; private set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,160 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Reactive.Linq; |
|||
using Avalonia.Interactivity; |
|||
using Avalonia.LogicalTree; |
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
/// <summary>
|
|||
/// Control that represents and displays a notification.
|
|||
/// </summary>
|
|||
public class NotificationCard : ContentControl |
|||
{ |
|||
private bool _isClosed; |
|||
private bool _isClosing; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="NotificationCard"/> class.
|
|||
/// </summary>
|
|||
public NotificationCard() |
|||
{ |
|||
this.GetObservable(IsClosedProperty) |
|||
.Subscribe(x => |
|||
{ |
|||
if (!IsClosing && !IsClosed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
RaiseEvent(new RoutedEventArgs(NotificationClosedEvent)); |
|||
}); |
|||
|
|||
this.GetObservable(ContentProperty) |
|||
.OfType<Notification>() |
|||
.Subscribe(x => |
|||
{ |
|||
switch (x.Type) |
|||
{ |
|||
case NotificationType.Error: |
|||
PseudoClasses.Add(":error"); |
|||
break; |
|||
|
|||
case NotificationType.Information: |
|||
PseudoClasses.Add(":information"); |
|||
break; |
|||
|
|||
case NotificationType.Success: |
|||
PseudoClasses.Add(":success"); |
|||
break; |
|||
|
|||
case NotificationType.Warning: |
|||
PseudoClasses.Add(":warning"); |
|||
break; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines if the notification is already closing.
|
|||
/// </summary>
|
|||
public bool IsClosing |
|||
{ |
|||
get { return _isClosing; } |
|||
private set { SetAndRaise(IsClosingProperty, ref _isClosing, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="IsClosing"/> property.
|
|||
/// </summary>
|
|||
public static readonly DirectProperty<NotificationCard, bool> IsClosingProperty = |
|||
AvaloniaProperty.RegisterDirect<NotificationCard, bool>(nameof(IsClosing), o => o.IsClosing); |
|||
|
|||
/// <summary>
|
|||
/// Determines if the notification is closed.
|
|||
/// </summary>
|
|||
public bool IsClosed |
|||
{ |
|||
get { return _isClosed; } |
|||
set { SetAndRaise(IsClosedProperty, ref _isClosed, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="IsClosed"/> property.
|
|||
/// </summary>
|
|||
public static readonly DirectProperty<NotificationCard, bool> IsClosedProperty = |
|||
AvaloniaProperty.RegisterDirect<NotificationCard, bool>(nameof(IsClosed), o => o.IsClosed, (o, v) => o.IsClosed = v); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="NotificationClosed"/> event.
|
|||
/// </summary>
|
|||
public static readonly RoutedEvent<RoutedEventArgs> NotificationClosedEvent = |
|||
RoutedEvent.Register<NotificationCard, RoutedEventArgs>(nameof(NotificationClosed), RoutingStrategies.Bubble); |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Raised when the <see cref="NotificationCard"/> has closed.
|
|||
/// </summary>
|
|||
public event EventHandler<RoutedEventArgs> NotificationClosed |
|||
{ |
|||
add { AddHandler(NotificationClosedEvent, value); } |
|||
remove { RemoveHandler(NotificationClosedEvent, value); } |
|||
} |
|||
|
|||
public static bool GetCloseOnClick(Button obj) |
|||
{ |
|||
return (bool)obj.GetValue(CloseOnClickProperty); |
|||
} |
|||
|
|||
public static void SetCloseOnClick(Button obj, bool value) |
|||
{ |
|||
obj.SetValue(CloseOnClickProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Defines the CloseOnClick property.
|
|||
/// </summary>
|
|||
public static readonly AvaloniaProperty CloseOnClickProperty = |
|||
AvaloniaProperty.RegisterAttached<Button, bool>("CloseOnClick", typeof(NotificationCard), validate: CloseOnClickChanged); |
|||
|
|||
private static bool CloseOnClickChanged(Button button, bool value) |
|||
{ |
|||
if (value) |
|||
{ |
|||
button.Click += Button_Click; |
|||
} |
|||
else |
|||
{ |
|||
button.Click -= Button_Click; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Called when a button inside the Notification is clicked.
|
|||
/// </summary>
|
|||
private static void Button_Click(object sender, RoutedEventArgs e) |
|||
{ |
|||
var btn = sender as ILogical; |
|||
var notification = btn.GetLogicalAncestors().OfType<NotificationCard>().FirstOrDefault(); |
|||
notification?.Close(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Closes the <see cref="NotificationCard"/>.
|
|||
/// </summary>
|
|||
public void Close() |
|||
{ |
|||
if (IsClosing) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
IsClosing = true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
/// <summary>
|
|||
/// Describes the possible positions for <see cref="Notification"/>s that are displayed by a
|
|||
/// <see cref="WindowNotificationManager"/>.
|
|||
/// </summary>
|
|||
public enum NotificationPosition |
|||
{ |
|||
TopLeft, |
|||
TopRight, |
|||
BottomLeft, |
|||
BottomRight |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
/// <summary>
|
|||
/// Enumeration of types for <see cref="INotification"/>.
|
|||
/// </summary>
|
|||
public enum NotificationType |
|||
{ |
|||
Information, |
|||
Success, |
|||
Warning, |
|||
Error |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using Avalonia.Layout; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Implements a <see cref="StackPanel"/> where the flow direction of its items can be reversed.
|
|||
/// </summary>
|
|||
public class ReversibleStackPanel : StackPanel |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the <see cref="ReverseOrder"/> property.
|
|||
/// </summary>
|
|||
public static readonly StyledProperty<bool> ReverseOrderProperty = |
|||
AvaloniaProperty.Register<ReversibleStackPanel, bool>(nameof(ReverseOrder)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets if the child controls will be layed out in reverse order.
|
|||
/// </summary>
|
|||
public bool ReverseOrder |
|||
{ |
|||
get => GetValue(ReverseOrderProperty); |
|||
set => SetValue(ReverseOrderProperty, value); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
var orientation = Orientation; |
|||
var spacing = Spacing; |
|||
var finalRect = new Rect(finalSize); |
|||
var pos = 0.0; |
|||
|
|||
var children = ReverseOrder ? Children.Reverse() : Children; |
|||
|
|||
foreach (Control child in children) |
|||
{ |
|||
double childWidth = child.DesiredSize.Width; |
|||
double childHeight = child.DesiredSize.Height; |
|||
|
|||
if (orientation == Orientation.Vertical) |
|||
{ |
|||
var rect = new Rect(0, pos, childWidth, childHeight) |
|||
.Align(finalRect, child.HorizontalAlignment, VerticalAlignment.Top); |
|||
ArrangeChild(child, rect, finalSize, orientation); |
|||
pos += childHeight + spacing; |
|||
} |
|||
else |
|||
{ |
|||
var rect = new Rect(pos, 0, childWidth, childHeight) |
|||
.Align(finalRect, HorizontalAlignment.Left, child.VerticalAlignment); |
|||
ArrangeChild(child, rect, finalSize, orientation); |
|||
pos += childWidth + spacing; |
|||
} |
|||
} |
|||
|
|||
return finalSize; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,163 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Linq; |
|||
using System.Reactive.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Controls.Primitives; |
|||
using Avalonia.VisualTree; |
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
/// <summary>
|
|||
/// An <see cref="INotificationManager"/> that displays notifications in a <see cref="Window"/>.
|
|||
/// </summary>
|
|||
public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager |
|||
{ |
|||
private IList _items; |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="Position"/> property.
|
|||
/// </summary>
|
|||
public static readonly StyledProperty<NotificationPosition> PositionProperty = |
|||
AvaloniaProperty.Register<WindowNotificationManager, NotificationPosition>(nameof(Position), NotificationPosition.TopRight); |
|||
|
|||
/// <summary>
|
|||
/// Defines which corner of the screen notifications can be displayed in.
|
|||
/// </summary>
|
|||
/// <seealso cref="NotificationPosition"/>
|
|||
public NotificationPosition Position |
|||
{ |
|||
get { return GetValue(PositionProperty); } |
|||
set { SetValue(PositionProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="MaxItems"/> property.
|
|||
/// </summary>
|
|||
public static readonly StyledProperty<int> MaxItemsProperty = |
|||
AvaloniaProperty.Register<WindowNotificationManager, int>(nameof(MaxItems), 5); |
|||
|
|||
/// <summary>
|
|||
/// Defines the maximum number of notifications visible at once.
|
|||
/// </summary>
|
|||
public int MaxItems |
|||
{ |
|||
get { return GetValue(MaxItemsProperty); } |
|||
set { SetValue(MaxItemsProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="WindowNotificationManager"/> class.
|
|||
/// </summary>
|
|||
/// <param name="host">The window that will host the control.</param>
|
|||
public WindowNotificationManager(Window host) |
|||
{ |
|||
if (VisualChildren.Count != 0) |
|||
{ |
|||
Install(host); |
|||
} |
|||
else |
|||
{ |
|||
Observable.FromEventPattern<TemplateAppliedEventArgs>(host, nameof(host.TemplateApplied)).Take(1) |
|||
.Subscribe(_ => |
|||
{ |
|||
Install(host); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
static WindowNotificationManager() |
|||
{ |
|||
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopLeft, ":topleft"); |
|||
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopRight, ":topright"); |
|||
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomLeft, ":bottomleft"); |
|||
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomRight, ":bottomright"); |
|||
|
|||
HorizontalAlignmentProperty.OverrideDefaultValue<WindowNotificationManager>(Layout.HorizontalAlignment.Stretch); |
|||
VerticalAlignmentProperty.OverrideDefaultValue<WindowNotificationManager>(Layout.VerticalAlignment.Stretch); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
base.OnTemplateApplied(e); |
|||
|
|||
var itemsControl = e.NameScope.Find<Panel>("PART_Items"); |
|||
_items = itemsControl?.Children; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public void Show(INotification content) |
|||
{ |
|||
Show(content as object); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public async void Show(object content) |
|||
{ |
|||
var notification = content as INotification; |
|||
|
|||
var notificationControl = new NotificationCard |
|||
{ |
|||
Content = content |
|||
}; |
|||
|
|||
if (notification != null) |
|||
{ |
|||
notificationControl.NotificationClosed += (sender, args) => |
|||
{ |
|||
notification.OnClose?.Invoke(); |
|||
|
|||
_items.Remove(sender); |
|||
}; |
|||
} |
|||
|
|||
notificationControl.PointerPressed += (sender, args) => |
|||
{ |
|||
if (notification != null && notification.OnClick != null) |
|||
{ |
|||
notification.OnClick.Invoke(); |
|||
} |
|||
|
|||
(sender as NotificationCard)?.Close(); |
|||
}; |
|||
|
|||
_items.Add(notificationControl); |
|||
|
|||
if (_items.OfType<NotificationCard>().Count(i => !i.IsClosing) > MaxItems) |
|||
{ |
|||
_items.OfType<NotificationCard>().First(i => !i.IsClosing).Close(); |
|||
} |
|||
|
|||
if (notification != null && notification.Expiration == TimeSpan.Zero) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
await Task.Delay(notification?.Expiration ?? TimeSpan.FromSeconds(5)); |
|||
|
|||
notificationControl.Close(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Installs the <see cref="WindowNotificationManager"/> within the <see cref="AdornerLayer"/>
|
|||
/// of the host <see cref="Window"/>.
|
|||
/// </summary>
|
|||
/// <param name="host">The <see cref="Window"/> that will be the host.</param>
|
|||
private void Install(Window host) |
|||
{ |
|||
var adornerLayer = host.GetVisualDescendants() |
|||
.OfType<AdornerDecorator>() |
|||
.FirstOrDefault() |
|||
?.AdornerLayer; |
|||
|
|||
if (adornerLayer != null) |
|||
{ |
|||
adornerLayer.Children.Add(this); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
<Styles xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
|||
<Style Selector="NotificationCard"> |
|||
<Setter Property="UseLayoutRounding" Value="True"/> |
|||
<Setter Property="Width" Value="350"/> |
|||
<Setter Property="FontSize" Value="14"/> |
|||
<Setter Property="Foreground" Value="White"/> |
|||
<Setter Property="RenderTransformOrigin" Value="50%,75%"/> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<LayoutTransformControl Name="PART_LayoutTransformControl" UseRenderTransform="True"> |
|||
<Border Background="{TemplateBinding Background}" |
|||
BorderBrush="{TemplateBinding BorderBrush}" |
|||
BorderThickness="{TemplateBinding BorderThickness}" |
|||
Margin="8,8,0,0"> |
|||
<ContentControl MinHeight="150" Content="{TemplateBinding Content}" /> |
|||
</Border> |
|||
</LayoutTransformControl> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
|
|||
<Style.Animations> |
|||
<Animation Duration="0:0:0.45" Easing="QuadraticEaseIn" FillMode="Forward"> |
|||
<KeyFrame Cue="0%"> |
|||
<Setter Property="Opacity" Value="0"/> |
|||
<Setter Property="TranslateTransform.Y" Value="20"/> |
|||
<Setter Property="ScaleTransform.ScaleX" Value="0.85"/> |
|||
<Setter Property="ScaleTransform.ScaleY" Value="0.85"/> |
|||
</KeyFrame> |
|||
<KeyFrame Cue="30%"> |
|||
<Setter Property="TranslateTransform.Y" Value="-20"/> |
|||
</KeyFrame> |
|||
<KeyFrame Cue="100%"> |
|||
<Setter Property="Opacity" Value="1"/> |
|||
<Setter Property="TranslateTransform.Y" Value="0"/> |
|||
<Setter Property="ScaleTransform.ScaleX" Value="1"/> |
|||
<Setter Property="ScaleTransform.ScaleY" Value="1"/> |
|||
</KeyFrame> |
|||
</Animation> |
|||
</Style.Animations> |
|||
</Style> |
|||
|
|||
<Style Selector="NotificationCard[IsClosing=true] /template/ LayoutTransformControl#PART_LayoutTransformControl"> |
|||
<Setter Property="RenderTransformOrigin" Value="50%,0%"/> |
|||
<Style.Animations> |
|||
<Animation Duration="0:0:0.75" Easing="QuadraticEaseOut" FillMode="Forward"> |
|||
<KeyFrame Cue="0%"> |
|||
<Setter Property="TranslateTransform.X" Value="0"/> |
|||
<Setter Property="ScaleTransform.ScaleY" Value="1"/> |
|||
</KeyFrame> |
|||
<KeyFrame Cue="70%"> |
|||
<Setter Property="TranslateTransform.X" Value="800"/> |
|||
<Setter Property="ScaleTransform.ScaleY" Value="1"/> |
|||
</KeyFrame> |
|||
<KeyFrame Cue="100%"> |
|||
<Setter Property="ScaleTransform.ScaleY" Value="0"/> |
|||
<Setter Property="TranslateTransform.X" Value="800"/> |
|||
</KeyFrame> |
|||
</Animation> |
|||
</Style.Animations> |
|||
</Style> |
|||
|
|||
<Style Selector="NotificationCard[IsClosing=true]"> |
|||
<Style.Animations> |
|||
<Animation Duration="0:0:1.25" Easing="QuadraticEaseOut" FillMode="Forward"> |
|||
<KeyFrame Cue="100%"> |
|||
<Setter Property="IsClosed" Value="True"/> |
|||
</KeyFrame> |
|||
</Animation> |
|||
</Style.Animations> |
|||
</Style> |
|||
|
|||
<Style Selector="NotificationCard"> |
|||
<Setter Property="Background" Value="{DynamicResource NotificationCardBackgroundBrush}"/> |
|||
</Style> |
|||
<Style Selector="NotificationCard:information"> |
|||
<Setter Property="Background" Value="{DynamicResource NotificationCardInformationBackgroundBrush}"/> |
|||
</Style> |
|||
<Style Selector="NotificationCard:success"> |
|||
<Setter Property="Background" Value="{DynamicResource NotificationCardSuccessBackgroundBrush}"/> |
|||
</Style> |
|||
<Style Selector="NotificationCard:warning"> |
|||
<Setter Property="Background" Value="{DynamicResource NotificationCardWarningBackgroundBrush}"/> |
|||
</Style> |
|||
<Style Selector="NotificationCard:error"> |
|||
<Setter Property="Background" Value="{DynamicResource NotificationCardErrorBackgroundBrush}"/> |
|||
</Style> |
|||
</Styles> |
|||
@ -0,0 +1,45 @@ |
|||
<Styles xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
|||
<Style Selector="WindowNotificationManager"> |
|||
<Setter Property="Margin" Value="0 0 8 8"/> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<ReversibleStackPanel Name="PART_Items"> |
|||
<ReversibleStackPanel.DataTemplates> |
|||
<DataTemplate DataType="INotification"> |
|||
<StackPanel Spacing="8" Margin="12"> |
|||
<TextBlock Text="{Binding Title}" FontWeight="Medium" /> |
|||
<TextBlock MaxHeight="80" Text="{Binding Message}" TextWrapping="Wrap" Margin="0,0,12,0"/> |
|||
</StackPanel> |
|||
</DataTemplate> |
|||
<DataTemplate DataType="x:String"> |
|||
<TextBlock Text="{Binding }" Margin="12" /> |
|||
</DataTemplate> |
|||
</ReversibleStackPanel.DataTemplates> |
|||
</ReversibleStackPanel> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<Style Selector="WindowNotificationManager:topleft /template/ ReversibleStackPanel#PART_Items"> |
|||
<Setter Property="VerticalAlignment" Value="Top"/> |
|||
<Setter Property="HorizontalAlignment" Value="Left"/> |
|||
</Style> |
|||
|
|||
<Style Selector="WindowNotificationManager:topright /template/ ReversibleStackPanel#PART_Items"> |
|||
<Setter Property="VerticalAlignment" Value="Top"/> |
|||
<Setter Property="HorizontalAlignment" Value="Right"/> |
|||
</Style> |
|||
|
|||
<Style Selector="WindowNotificationManager:bottomleft /template/ ReversibleStackPanel#PART_Items"> |
|||
<Setter Property="ReverseOrder" Value="True"/> |
|||
<Setter Property="VerticalAlignment" Value="Bottom"/> |
|||
<Setter Property="HorizontalAlignment" Value="Left"/> |
|||
</Style> |
|||
|
|||
<Style Selector="WindowNotificationManager:bottomright /template/ ReversibleStackPanel#PART_Items"> |
|||
<Setter Property="ReverseOrder" Value="True"/> |
|||
<Setter Property="VerticalAlignment" Value="Bottom"/> |
|||
<Setter Property="HorizontalAlignment" Value="Right"/> |
|||
</Style> |
|||
</Styles> |
|||
@ -1 +1 @@ |
|||
Subproject commit 7452b23169e4948907fa10e2c115b672897d0e04 |
|||
Subproject commit ab5526173722b8988bc5ca3c03c8752ce89c0975 |
|||
Loading…
Reference in new issue