From 7ce869475ed1aa3875ae6be4bad2db7885a799a1 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Tue, 25 Oct 2022 12:02:08 +0000 Subject: [PATCH 1/5] add notifcationmanager property to toplevel --- .../Pages/NotificationsPage.xaml.cs | 15 ++++++++++ .../ViewModels/MainWindowViewModel.cs | 30 ------------------- .../ViewModels/NotificationViewModel.cs | 29 ++++++++++++++++-- .../WindowNotificationManager.cs | 7 ++--- src/Avalonia.Controls/TopLevel.cs | 5 ++++ 5 files changed, 49 insertions(+), 37 deletions(-) diff --git a/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs b/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs index eadf92b602..be08af41e1 100644 --- a/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs @@ -1,18 +1,33 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using ControlCatalog.ViewModels; namespace ControlCatalog.Pages { public class NotificationsPage : UserControl { + private NotificationViewModel _viewModel; + public NotificationsPage() { this.InitializeComponent(); + + _viewModel = new NotificationViewModel(); + + DataContext = _viewModel; } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + _viewModel.NotificationManager = (VisualRoot as TopLevel)?.NotificationManager; + } } } diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index c2c2462246..72ba6cead0 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -26,23 +26,6 @@ namespace ControlCatalog.ViewModels public MainWindowViewModel(IManagedNotificationManager notificationManager) { - _notificationManager = notificationManager; - - ShowCustomManagedNotificationCommand = MiniCommand.Create(() => - { - NotificationManager.Show(new NotificationViewModel(NotificationManager) { Title = "Hey There!", Message = "Did you know that Avalonia now supports Custom In-Window Notifications?" }); - }); - - ShowManagedNotificationCommand = MiniCommand.Create(() => - { - NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Welcome", "Avalonia now supports Notifications.", NotificationType.Information)); - }); - - ShowNativeNotificationCommand = MiniCommand.Create(() => - { - NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Error", "Native Notifications are not quite ready. Coming soon.", NotificationType.Error)); - }); - AboutCommand = MiniCommand.CreateFromTask(async () => { var dialog = new AboutAvaloniaDialog(); @@ -52,7 +35,6 @@ namespace ControlCatalog.ViewModels await dialog.ShowDialog(mainWindow); } }); - ExitCommand = MiniCommand.Create(() => { (App.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.Shutdown(); @@ -143,24 +125,12 @@ namespace ControlCatalog.ViewModels set { this.RaiseAndSetIfChanged(ref _windowStates, value); } } - public IManagedNotificationManager NotificationManager - { - get { return _notificationManager; } - set { this.RaiseAndSetIfChanged(ref _notificationManager, value); } - } - public bool IsMenuItemChecked { get { return _isMenuItemChecked; } set { this.RaiseAndSetIfChanged(ref _isMenuItemChecked, value); } } - public MiniCommand ShowCustomManagedNotificationCommand { get; } - - public MiniCommand ShowManagedNotificationCommand { get; } - - public MiniCommand ShowNativeNotificationCommand { get; } - public MiniCommand AboutCommand { get; } public MiniCommand ExitCommand { get; } diff --git a/samples/ControlCatalog/ViewModels/NotificationViewModel.cs b/samples/ControlCatalog/ViewModels/NotificationViewModel.cs index b714c319a6..a31f164a2a 100644 --- a/samples/ControlCatalog/ViewModels/NotificationViewModel.cs +++ b/samples/ControlCatalog/ViewModels/NotificationViewModel.cs @@ -6,16 +6,33 @@ namespace ControlCatalog.ViewModels { public class NotificationViewModel { - public NotificationViewModel(INotificationManager manager) + public WindowNotificationManager? NotificationManager { get; set; } + + public NotificationViewModel() { + ShowCustomManagedNotificationCommand = MiniCommand.Create(() => + { + NotificationManager?.Show(new NotificationViewModel() { Title = "Hey There!", Message = "Did you know that Avalonia now supports Custom In-Window Notifications?" , NotificationManager = NotificationManager}); + }); + + ShowManagedNotificationCommand = MiniCommand.Create(() => + { + NotificationManager?.Show(new Avalonia.Controls.Notifications.Notification("Welcome", "Avalonia now supports Notifications.", NotificationType.Information)); + }); + + ShowNativeNotificationCommand = MiniCommand.Create(() => + { + NotificationManager?.Show(new Avalonia.Controls.Notifications.Notification("Error", "Native Notifications are not quite ready. Coming soon.", NotificationType.Error)); + }); + YesCommand = MiniCommand.Create(() => { - manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today.")); + NotificationManager?.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today.")); }); NoCommand = MiniCommand.Create(() => { - manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today. To find out more visit...")); + NotificationManager?.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today. To find out more visit...")); }); } @@ -26,5 +43,11 @@ namespace ControlCatalog.ViewModels public MiniCommand NoCommand { get; } + public MiniCommand ShowCustomManagedNotificationCommand { get; } + + public MiniCommand ShowManagedNotificationCommand { get; } + + public MiniCommand ShowNativeNotificationCommand { get; } + } } diff --git a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs index 630276767a..969b0a38b1 100644 --- a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs @@ -3,11 +3,10 @@ using System.Collections; using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Rendering; -using Avalonia.Data; using Avalonia.VisualTree; -using Avalonia.Controls.Metadata; namespace Avalonia.Controls.Notifications { @@ -55,7 +54,7 @@ namespace Avalonia.Controls.Notifications /// Initializes a new instance of the class. /// /// The window that will host the control. - public WindowNotificationManager(Window host) + public WindowNotificationManager(TopLevel host) { if (VisualChildren.Count != 0) { @@ -151,7 +150,7 @@ namespace Avalonia.Controls.Notifications /// of the host . /// /// The that will be the host. - private void Install(Window host) + private void Install(TemplatedControl host) { var adornerLayer = host.FindDescendantOfType()?.AdornerLayer; diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 47fc9d7988..f91d58ea13 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -1,6 +1,7 @@ using System; using System.Reactive.Linq; using Avalonia.Controls.Metadata; +using Avalonia.Controls.Notifications; using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives; using Avalonia.Input; @@ -95,6 +96,7 @@ namespace Avalonia.Controls private Border? _transparencyFallbackBorder; private TargetWeakEventSubscriber? _resourcesChangesSubscriber; private IStorageProvider? _storageProvider; + private WindowNotificationManager _notificationManager; /// /// Initializes static members of the class. @@ -151,6 +153,7 @@ namespace Avalonia.Controls _keyboardNavigationHandler = TryGetService(dependencyResolver); _renderInterface = TryGetService(dependencyResolver); _globalStyles = TryGetService(dependencyResolver); + _notificationManager = new WindowNotificationManager(this); Renderer = impl.CreateRenderer(this); @@ -328,6 +331,8 @@ namespace Avalonia.Controls IRenderTarget IRenderRoot.CreateRenderTarget() => CreateRenderTarget(); + public WindowNotificationManager NotificationManager => _notificationManager; + /// protected virtual IRenderTarget CreateRenderTarget() { From 6384c49cdb355eda065d34a9f730cdd405a9107e Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Tue, 25 Oct 2022 15:29:54 +0000 Subject: [PATCH 2/5] update tests --- .../Primitives/PopupRootTests.cs | 2 +- .../Primitives/PopupTests.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs index 6d3351d2b2..9367e0e94e 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs @@ -88,7 +88,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.IsType(visualLayerManager); - var contentPresenter = visualLayerManager.VisualChildren.Single(); + var contentPresenter = visualLayerManager.VisualChildren.Take(1).Single(); Assert.IsType(contentPresenter); diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index 5f91f2e2a1..3a83bd4f43 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -345,6 +345,9 @@ namespace Avalonia.Controls.UnitTests.Primitives "ContentPresenter", "ContentPresenter", "Border", + "AdornerLayer", + "WindowNotificationManager", + "ReversibleStackPanel" }, types); } @@ -353,6 +356,8 @@ namespace Avalonia.Controls.UnitTests.Primitives .OfType() .Select(x => x.TemplatedParent).ToList(); + var notificationManager = (popup.Host as TopLevel)?.NotificationManager; + if (UsePopupHost) { Assert.Equal( @@ -378,6 +383,9 @@ namespace Avalonia.Controls.UnitTests.Primitives popupRoot, target, null, + null, + null, + notificationManager, }, templatedParents); } @@ -438,6 +446,9 @@ namespace Avalonia.Controls.UnitTests.Primitives "ItemsPresenter", "StackPanel", "Border", + "AdornerLayer", + "WindowNotificationManager", + "ReversibleStackPanel" }, types); } @@ -446,6 +457,8 @@ namespace Avalonia.Controls.UnitTests.Primitives .OfType() .Select(x => x.TemplatedParent).ToList(); + var notificationManager = (popup.Host as TopLevel)?.NotificationManager; + if (UsePopupHost) { Assert.Equal( @@ -473,6 +486,9 @@ namespace Avalonia.Controls.UnitTests.Primitives target, target, null, + null, + null, + notificationManager, }, templatedParents); } From ab9835ef88bce415318f50021a77fb1eb8c9f988 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 10 Nov 2022 08:53:53 +0000 Subject: [PATCH 3/5] made notification manager lazy --- src/Avalonia.Controls/TopLevel.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index f91d58ea13..d538f05eec 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -96,7 +96,7 @@ namespace Avalonia.Controls private Border? _transparencyFallbackBorder; private TargetWeakEventSubscriber? _resourcesChangesSubscriber; private IStorageProvider? _storageProvider; - private WindowNotificationManager _notificationManager; + private WindowNotificationManager? _notificationManager; /// /// Initializes static members of the class. @@ -153,7 +153,6 @@ namespace Avalonia.Controls _keyboardNavigationHandler = TryGetService(dependencyResolver); _renderInterface = TryGetService(dependencyResolver); _globalStyles = TryGetService(dependencyResolver); - _notificationManager = new WindowNotificationManager(this); Renderer = impl.CreateRenderer(this); @@ -331,7 +330,17 @@ namespace Avalonia.Controls IRenderTarget IRenderRoot.CreateRenderTarget() => CreateRenderTarget(); - public WindowNotificationManager NotificationManager => _notificationManager; + public WindowNotificationManager NotificationManager + { + get + { + if(_notificationManager == null) + { + _notificationManager = new WindowNotificationManager(this); + } + return _notificationManager; + } + } /// protected virtual IRenderTarget CreateRenderTarget() From 588001d3e78145c3c6322da6fba4c8f9b2cc3054 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 10 Nov 2022 09:07:54 +0000 Subject: [PATCH 4/5] revert tests --- .../Primitives/PopupRootTests.cs | 2 +- .../Primitives/PopupTests.cs | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs index 9367e0e94e..6d3351d2b2 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs @@ -88,7 +88,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.IsType(visualLayerManager); - var contentPresenter = visualLayerManager.VisualChildren.Take(1).Single(); + var contentPresenter = visualLayerManager.VisualChildren.Single(); Assert.IsType(contentPresenter); diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index 3a83bd4f43..5f91f2e2a1 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -345,9 +345,6 @@ namespace Avalonia.Controls.UnitTests.Primitives "ContentPresenter", "ContentPresenter", "Border", - "AdornerLayer", - "WindowNotificationManager", - "ReversibleStackPanel" }, types); } @@ -356,8 +353,6 @@ namespace Avalonia.Controls.UnitTests.Primitives .OfType() .Select(x => x.TemplatedParent).ToList(); - var notificationManager = (popup.Host as TopLevel)?.NotificationManager; - if (UsePopupHost) { Assert.Equal( @@ -383,9 +378,6 @@ namespace Avalonia.Controls.UnitTests.Primitives popupRoot, target, null, - null, - null, - notificationManager, }, templatedParents); } @@ -446,9 +438,6 @@ namespace Avalonia.Controls.UnitTests.Primitives "ItemsPresenter", "StackPanel", "Border", - "AdornerLayer", - "WindowNotificationManager", - "ReversibleStackPanel" }, types); } @@ -457,8 +446,6 @@ namespace Avalonia.Controls.UnitTests.Primitives .OfType() .Select(x => x.TemplatedParent).ToList(); - var notificationManager = (popup.Host as TopLevel)?.NotificationManager; - if (UsePopupHost) { Assert.Equal( @@ -486,9 +473,6 @@ namespace Avalonia.Controls.UnitTests.Primitives target, target, null, - null, - null, - notificationManager, }, templatedParents); } From 96d4a1588bba6bfca9cdebfcf9bf9a1c3394cf26 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 10 Nov 2022 18:30:06 +0000 Subject: [PATCH 5/5] remove notification manager property from toplevel. remove unused notification manager in control catalog main window --- samples/ControlCatalog/MainWindow.xaml.cs | 12 +----------- .../ControlCatalog/Pages/NotificationsPage.xaml.cs | 2 +- .../ViewModels/MainWindowViewModel.cs | 2 +- .../Notifications/WindowNotificationManager.cs | 12 ++---------- src/Avalonia.Controls/TopLevel.cs | 13 ------------- 5 files changed, 5 insertions(+), 36 deletions(-) diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index c61296ac8f..c589f41442 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -11,23 +11,13 @@ namespace ControlCatalog { public class MainWindow : Window { - private WindowNotificationManager _notificationArea; private NativeMenu? _recentMenu; public MainWindow() { this.InitializeComponent(); - //Renderer.DrawFps = true; - //Renderer.DrawDirtyRects = Renderer.DrawFps = true; - - _notificationArea = new WindowNotificationManager(this) - { - Position = NotificationPosition.TopRight, - MaxItems = 3 - }; - - DataContext = new MainWindowViewModel(_notificationArea); + DataContext = new MainWindowViewModel(); _recentMenu = ((NativeMenu.GetMenu(this)?.Items[0] as NativeMenuItem)?.Menu?.Items[2] as NativeMenuItem)?.Menu; } diff --git a/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs b/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs index be08af41e1..bfd49a2c00 100644 --- a/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/NotificationsPage.xaml.cs @@ -27,7 +27,7 @@ namespace ControlCatalog.Pages { base.OnAttachedToVisualTree(e); - _viewModel.NotificationManager = (VisualRoot as TopLevel)?.NotificationManager; + _viewModel.NotificationManager = new Avalonia.Controls.Notifications.WindowNotificationManager(VisualRoot as TopLevel); } } } diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index 72ba6cead0..b79eff780c 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -24,7 +24,7 @@ namespace ControlCatalog.ViewModels private bool _preferSystemChromeEnabled; private double _titleBarHeight; - public MainWindowViewModel(IManagedNotificationManager notificationManager) + public MainWindowViewModel() { AboutCommand = MiniCommand.CreateFromTask(async () => { diff --git a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs index 969b0a38b1..45beaa0b2f 100644 --- a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs @@ -54,20 +54,12 @@ namespace Avalonia.Controls.Notifications /// Initializes a new instance of the class. /// /// The window that will host the control. - public WindowNotificationManager(TopLevel host) + public WindowNotificationManager(TopLevel? host) { - if (VisualChildren.Count != 0) + if (host != null) { Install(host); } - else - { - Observable.FromEventPattern(host, nameof(host.TemplateApplied)).Take(1) - .Subscribe(_ => - { - Install(host); - }); - } UpdatePseudoClasses(Position); } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index d538f05eec..6804c9ecb9 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -96,7 +96,6 @@ namespace Avalonia.Controls private Border? _transparencyFallbackBorder; private TargetWeakEventSubscriber? _resourcesChangesSubscriber; private IStorageProvider? _storageProvider; - private WindowNotificationManager? _notificationManager; /// /// Initializes static members of the class. @@ -330,18 +329,6 @@ namespace Avalonia.Controls IRenderTarget IRenderRoot.CreateRenderTarget() => CreateRenderTarget(); - public WindowNotificationManager NotificationManager - { - get - { - if(_notificationManager == null) - { - _notificationManager = new WindowNotificationManager(this); - } - return _notificationManager; - } - } - /// protected virtual IRenderTarget CreateRenderTarget() {