4 changed files with 272 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
interface INotificationManager |
|||
{ |
|||
void Show(object content, string areaName = "", TimeSpan? expirationTime = null, Action onClick = null, Action onClose = null); |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Controls.Primitives; |
|||
using Avalonia.Interactivity; |
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
public class Notification : ContentControl |
|||
{ |
|||
private TimeSpan _closingAnimationTime = TimeSpan.Zero; |
|||
|
|||
public bool IsClosing { get; set; } |
|||
|
|||
public static readonly RoutedEvent NotificationCloseInvokedEvent = EventManager.RegisterRoutedEvent( |
|||
"NotificationCloseInvoked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Notification)); |
|||
|
|||
public static readonly RoutedEvent NotificationClosedEvent = EventManager.RegisterRoutedEvent( |
|||
"NotificationClosed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Notification)); |
|||
|
|||
public event RoutedEventHandler NotificationCloseInvoked |
|||
{ |
|||
add { AddHandler(NotificationCloseInvokedEvent, value); } |
|||
remove { RemoveHandler(NotificationCloseInvokedEvent, value); } |
|||
} |
|||
|
|||
public event RoutedEventHandler NotificationClosed |
|||
{ |
|||
add { AddHandler(NotificationClosedEvent, value); } |
|||
remove { RemoveHandler(NotificationClosedEvent, value); } |
|||
} |
|||
|
|||
public static bool GetCloseOnClick(DependencyObject obj) |
|||
{ |
|||
return (bool)obj.GetValue(CloseOnClickProperty); |
|||
} |
|||
|
|||
public static void SetCloseOnClick(DependencyObject obj, bool value) |
|||
{ |
|||
obj.SetValue(CloseOnClickProperty, value); |
|||
} |
|||
|
|||
public static readonly DependencyProperty CloseOnClickProperty = |
|||
DependencyProperty.RegisterAttached("CloseOnClick", typeof(bool), typeof(Notification), new FrameworkPropertyMetadata(false, CloseOnClickChanged)); |
|||
|
|||
private static void CloseOnClickChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) |
|||
{ |
|||
var button = dependencyObject as Button; |
|||
if (button == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var value = (bool)dependencyPropertyChangedEventArgs.NewValue; |
|||
|
|||
if (value) |
|||
{ |
|||
button.Click += (sender, args) => |
|||
{ |
|||
var notification = VisualTreeHelperExtensions.GetParent<Notification>(button); |
|||
notification?.Close(); |
|||
}; |
|||
} |
|||
} |
|||
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
base.OnTemplateApplied(e); |
|||
var closeButton = this.FindControl<Button>("PART_CloseButton"); |
|||
if (closeButton != null) |
|||
closeButton.Click += OnCloseButtonOnClick; |
|||
|
|||
//var storyboards = Template.Triggers.OfType<EventTrigger>().FirstOrDefault(t => t.RoutedEvent == NotificationCloseInvokedEvent)?.Actions.OfType<BeginStoryboard>().Select(a => a.Storyboard);
|
|||
//_closingAnimationTime = new TimeSpan(storyboards?.Max(s => Math.Min((s.Duration.HasTimeSpan ? s.Duration.TimeSpan + (s.BeginTime ?? TimeSpan.Zero) : TimeSpan.MaxValue).Ticks, s.Children.Select(ch => ch.Duration.TimeSpan + (s.BeginTime ?? TimeSpan.Zero)).Max().Ticks)) ?? 0);
|
|||
|
|||
} |
|||
|
|||
private void OnCloseButtonOnClick(object sender, RoutedEventArgs args) |
|||
{ |
|||
var button = sender as Button; |
|||
if (button == null) |
|||
return; |
|||
|
|||
button.Click -= OnCloseButtonOnClick; |
|||
Close(); |
|||
} |
|||
|
|||
public async void Close() |
|||
{ |
|||
if (IsClosing) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
IsClosing = true; |
|||
|
|||
RaiseEvent(new RoutedEventArgs(NotificationCloseInvokedEvent)); |
|||
await Task.Delay(_closingAnimationTime); |
|||
RaiseEvent(new RoutedEventArgs(NotificationClosedEvent)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using Avalonia.Controls.Primitives; |
|||
using Avalonia.Interactivity; |
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
public class NotificationArea : TemplatedControl |
|||
{ |
|||
private IList _items; |
|||
|
|||
public NotificationPosition Position |
|||
{ |
|||
get { return (NotificationPosition)GetValue(PositionProperty); } |
|||
set { SetValue(PositionProperty, value); } |
|||
} |
|||
|
|||
public static readonly AvaloniaProperty PositionProperty = |
|||
AvaloniaProperty.Register<NotificationArea, NotificationPosition>(nameof(Position), NotificationPosition.BottomRight); |
|||
|
|||
public int MaxItems |
|||
{ |
|||
get { return (int)GetValue(MaxItemsProperty); } |
|||
set { SetValue(MaxItemsProperty, value); } |
|||
} |
|||
|
|||
public static readonly AvaloniaProperty MaxItemsProperty = |
|||
AvaloniaProperty.Register<NotificationArea, int>(nameof(MaxItems), int.MaxValue); |
|||
|
|||
public NotificationArea() |
|||
{ |
|||
NotificationManager.AddArea(this); |
|||
} |
|||
|
|||
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) |
|||
{ |
|||
base.OnTemplateApplied(e); |
|||
|
|||
var itemsControl = this.FindControl<Panel>("PART_Items"); |
|||
_items = itemsControl?.Children; |
|||
} |
|||
|
|||
public async void Show(object content, TimeSpan expirationTime, Action onClick, Action onClose) |
|||
{ |
|||
var notification = new Notification |
|||
{ |
|||
Content = content |
|||
}; |
|||
|
|||
notification.PointerPressed += (sender, args) => |
|||
{ |
|||
if (onClick != null) |
|||
{ |
|||
onClick.Invoke(); |
|||
(sender as Notification)?.Close(); |
|||
} |
|||
}; |
|||
notification.NotificationClosed += (sender, args) => onClose?.Invoke(); |
|||
notification.NotificationClosed += OnNotificationClosed; |
|||
|
|||
/* if (!IsLoaded) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var w = Window.GetWindow(this); |
|||
var x = PresentationSource.FromVisual(w); |
|||
if (x == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
lock (_items) |
|||
{ |
|||
_items.Add(notification); |
|||
|
|||
if (_items.OfType<Notification>().Count(i => !i.IsClosing) > MaxItems) |
|||
{ |
|||
_items.OfType<Notification>().First(i => !i.IsClosing).Close(); |
|||
} |
|||
} |
|||
|
|||
if (expirationTime == TimeSpan.MaxValue) |
|||
{ |
|||
return; |
|||
} |
|||
await Task.Delay(expirationTime); |
|||
|
|||
notification.Close();*/ |
|||
} |
|||
|
|||
private void OnNotificationClosed(object sender, RoutedEventArgs routedEventArgs) |
|||
{ |
|||
var notification = sender as Notification; |
|||
_items.Remove(notification); |
|||
} |
|||
} |
|||
|
|||
public enum NotificationPosition |
|||
{ |
|||
TopLeft, |
|||
TopRight, |
|||
BottomLeft, |
|||
BottomRight |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Avalonia.Threading; |
|||
|
|||
namespace Avalonia.Controls.Notifications |
|||
{ |
|||
public class NotificationManager : INotificationManager |
|||
{ |
|||
private static readonly List<NotificationArea> Areas = new List<NotificationArea>(); |
|||
private static Window _window; |
|||
|
|||
public NotificationManager() |
|||
{ |
|||
} |
|||
|
|||
public void Show(object content, string areaName = "", TimeSpan? expirationTime = null, Action onClick = null, |
|||
Action onClose = null) |
|||
{ |
|||
if (!Dispatcher.UIThread.CheckAccess()) |
|||
{ |
|||
Dispatcher.UIThread.Post( |
|||
new Action(() => Show(content, areaName, expirationTime, onClick, onClose))); |
|||
return; |
|||
} |
|||
|
|||
if (expirationTime == null) |
|||
expirationTime = TimeSpan.FromSeconds(5); |
|||
|
|||
if (areaName == string.Empty && _window == null) |
|||
{ |
|||
var workArea = Application.Current.MainWindow.Screens.Primary.WorkingArea; |
|||
|
|||
_window = new Window |
|||
{ |
|||
Position = workArea.Position, |
|||
Width = workArea.Width, |
|||
Height = workArea.Height |
|||
}; |
|||
|
|||
_window.Show(); |
|||
} |
|||
|
|||
foreach (var area in Areas.Where(a => a.Name == areaName)) |
|||
{ |
|||
area.Show(content, (TimeSpan)expirationTime, onClick, onClose); |
|||
} |
|||
} |
|||
|
|||
internal static void AddArea(NotificationArea area) |
|||
{ |
|||
Areas.Add(area); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue