From c01af45f7f23ac8929d53bdcb77d1566e8d271b1 Mon Sep 17 00:00:00 2001 From: Daniel Clark Date: Thu, 11 Feb 2021 00:56:52 +0000 Subject: [PATCH] Added ContextMenu support --- .../NotificationFlyoutSample/Shell.xaml | 3 ++ .../ContextMenuFlyoutHost.cs | 39 +++++++++++---- .../ContextMenuFlyoutHost.xaml | 4 +- .../NotificationFlyout/NotificationFlyout.cs | 34 +++++++++++-- .../NotificationFlyoutHost.cs | 1 + .../NotificationFlyoutApplication.cs | 27 +++++++---- .../NotificationFlyoutContextMenuXamlHost.cs | 48 +++++++++++++++++-- .../NotificationFlyoutXamlHost.cs | 43 ++++++----------- .../NotificationFlyout/XamlHostWindow.cs | 32 ++++++++----- 9 files changed, 163 insertions(+), 68 deletions(-) diff --git a/samples/NotificationFlyoutSample/NotificationFlyoutSample/Shell.xaml b/samples/NotificationFlyoutSample/NotificationFlyoutSample/Shell.xaml index a6e1b16..4f625e6 100644 --- a/samples/NotificationFlyoutSample/NotificationFlyoutSample/Shell.xaml +++ b/samples/NotificationFlyoutSample/NotificationFlyoutSample/Shell.xaml @@ -6,6 +6,9 @@ xmlns:muxc="using:Microsoft.UI.Xaml.Controls" IconSource="/Assets/Icon.ico" LightIconSource="/Assets/Icon-Light.ico"> + + + addedItems, IList removedItems = null) + { + if (_flyout == null) return; + + if (removedItems != null) + { + foreach (var item in removedItems) + { + _flyout.Items.Remove(item); + } + } + + foreach (var item in addedItems) + { + _flyout.Items.Add(item); + } } } } \ No newline at end of file diff --git a/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/ContextMenuFlyoutHost.xaml b/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/ContextMenuFlyoutHost.xaml index 418315b..87cf47d 100644 --- a/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/ContextMenuFlyoutHost.xaml +++ b/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/ContextMenuFlyoutHost.xaml @@ -8,9 +8,7 @@ - - - + diff --git a/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyout.cs b/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyout.cs index 17b6c27..3ef6557 100644 --- a/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyout.cs +++ b/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyout.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Markup; using Windows.UI.Xaml.Media; @@ -37,15 +38,17 @@ namespace NotificationFlyout.Uwp.UI.Controls typeof(IList), typeof(NotificationFlyout), new PropertyMetadata(null)); - internal event EventHandler ContentChanged; - internal event EventHandler IconSourceChanged; - internal event EventHandler RequestedThemeChanged; - public NotificationFlyout() { ContextMenuItems = new ObservableCollection(); + (ContextMenuItems as INotifyCollectionChanged).CollectionChanged += OnContextMenuItemsChanged; } + internal event EventHandler ContentChanged; + internal event EventHandler IconSourceChanged; + internal event EventHandler MenuItemsChanged; + internal event EventHandler RequestedThemeChanged; + public UIElement Content { get => (UIElement)GetValue(ContentProperty); @@ -99,6 +102,14 @@ namespace NotificationFlyout.Uwp.UI.Controls ContentChanged?.Invoke(this, EventArgs.Empty); } + private void OnContextMenuItemsChanged(object sender, NotifyCollectionChangedEventArgs args) + { + var addedItems = args.NewItems.Cast().ToList(); + var removedItems = args.NewItems.Cast().ToList(); + + MenuItemsChanged?.Invoke(this, new NotificationFlyoutMenuItemsChangedEventArgs(addedItems, removedItems)); + } + private void OnIconPropertyChanged() { IconSourceChanged?.Invoke(this, EventArgs.Empty); @@ -109,4 +120,17 @@ namespace NotificationFlyout.Uwp.UI.Controls RequestedThemeChanged?.Invoke(this, EventArgs.Empty); } } + + internal class NotificationFlyoutMenuItemsChangedEventArgs : EventArgs + { + public NotificationFlyoutMenuItemsChangedEventArgs(IList addedItems, IList removedItems) + { + AddedItems = addedItems; + RemovedItems = removedItems; + } + + public IList AddedItems { get; private set; } + + public IList RemovedItems { get; private set; } + } } diff --git a/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyoutHost.cs b/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyoutHost.cs index 6d533f2..57a1bab 100644 --- a/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyoutHost.cs +++ b/src/NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyoutHost.cs @@ -42,6 +42,7 @@ namespace NotificationFlyout.Uwp.UI.Controls _placement = placement; } + if (string.IsNullOrEmpty(placement)) return; VisualStateManager.GoToState(this, placement, true); } diff --git a/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutApplication.cs b/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutApplication.cs index 6e9b064..4e192df 100644 --- a/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutApplication.cs +++ b/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutApplication.cs @@ -1,4 +1,5 @@ -using System.Windows; +using System; +using System.Windows; using System.Windows.Markup; namespace NotificationFlyout.Wpf.UI.Controls @@ -11,12 +12,16 @@ namespace NotificationFlyout.Wpf.UI.Controls typeof(Uwp.UI.Controls.NotificationFlyout), typeof(NotificationFlyoutApplication), new PropertyMetadata(null, OnFlyoutPropertyChanged)); - private readonly NotificationFlyoutXamlHostWindow _xamlHost; - + private readonly ContextMenuXamlHost _contextMenuXamlHost; + private readonly NotificationFlyoutXamlHost _notificationFlyoutXamlHost; public NotificationFlyoutApplication() { - _xamlHost = new NotificationFlyoutXamlHostWindow(); - _xamlHost.Show(); + _notificationFlyoutXamlHost = new NotificationFlyoutXamlHost(); + _notificationFlyoutXamlHost.ContextMenuRequested += OnContextMenuRequested; + _notificationFlyoutXamlHost.Show(); + + _contextMenuXamlHost = new ContextMenuXamlHost(); + _contextMenuXamlHost.Show(); } public Uwp.UI.Controls.NotificationFlyout Flyout @@ -27,12 +32,12 @@ namespace NotificationFlyout.Wpf.UI.Controls public void HideFlyout() { - _xamlHost.HideFlyout(); + _notificationFlyoutXamlHost.HideFlyout(); } public void ShowFlyout() { - _xamlHost.ShowFlyout(); + _notificationFlyoutXamlHost.ShowFlyout(); } private static void OnFlyoutPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) @@ -41,9 +46,15 @@ namespace NotificationFlyout.Wpf.UI.Controls sender?.OnFlyoutPropertyChanged(); } + private void OnContextMenuRequested(object sender, EventArgs args) + { + _contextMenuXamlHost?.ShowContextMenuFlyout(); + } + private void OnFlyoutPropertyChanged() { - _xamlHost.SetFlyout(Flyout); + _notificationFlyoutXamlHost.SetFlyout(Flyout); + _contextMenuXamlHost.SetFlyout(Flyout); } } } \ No newline at end of file diff --git a/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutContextMenuXamlHost.cs b/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutContextMenuXamlHost.cs index 67e836d..6c7b169 100644 --- a/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutContextMenuXamlHost.cs +++ b/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutContextMenuXamlHost.cs @@ -2,23 +2,29 @@ using NotificationFlyout.Wpf.UI.Extensions; using NotificationFlyout.Wpf.UI.Helpers; using System; +using System.Collections.Generic; +using Windows.UI.Xaml.Controls; namespace NotificationFlyout.Wpf.UI.Controls { internal class ContextMenuXamlHost : XamlHostWindow { + private Uwp.UI.Controls.NotificationFlyout _flyout; + public ContextMenuXamlHost() { Topmost = true; } - protected override void OnDeactivated(EventArgs args) + public void SetFlyout(Uwp.UI.Controls.NotificationFlyout flyout) { - var flyoutHost = GetHostContent(); - if (flyoutHost != null) + if (_flyout != null) { - flyoutHost.HideFlyout(); + _flyout.MenuItemsChanged -= OnContextMenuItemsChanged; } + + _flyout = flyout; + UpdateMenuItems(); } public void ShowContextMenuFlyout() @@ -34,5 +40,39 @@ namespace NotificationFlyout.Wpf.UI.Controls Activate(); } + + protected override void OnContentLoaded() + { + UpdateMenuItems(); + } + + protected override void OnDeactivated(EventArgs args) + { + var flyoutHost = GetHostContent(); + if (flyoutHost != null) + { + flyoutHost.HideFlyout(); + } + } + + private void OnContextMenuItemsChanged(object sender, NotificationFlyoutMenuItemsChangedEventArgs args) + { + UpdateMenuItems(args.AddedItems, args.RemovedItems); + } + + private void UpdateMenuItems() + { + if (_flyout == null) return; + UpdateMenuItems(_flyout.ContextMenuItems); + } + + private void UpdateMenuItems(IList addedItems, IList removedItems = default) + { + var flyoutHost = GetHostContent(); + if (flyoutHost != null) + { + flyoutHost.SetMenuItems(addedItems, removedItems); + } + } } } \ No newline at end of file diff --git a/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutXamlHost.cs b/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutXamlHost.cs index 2ca5100..2e26307 100644 --- a/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutXamlHost.cs +++ b/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutXamlHost.cs @@ -9,23 +9,17 @@ using System.Windows.Input; namespace NotificationFlyout.Wpf.UI.Controls { - internal class NotificationFlyoutXamlHostWindow : XamlHostWindow + internal class NotificationFlyoutXamlHost : XamlHostWindow { private const string ShellTrayHandleName = "Shell_TrayWnd"; - private ContextMenuXamlHost _contextMenuXamlHost; private Uwp.UI.Controls.NotificationFlyout _flyout; - private bool _isLoaded; - private NotificationIconHelper _notificationIconHelper; private SystemPersonalisationHelper _systemPersonalisationHelper; private TaskbarHelper _taskbarHelper; - public NotificationFlyoutXamlHostWindow() - { - Loaded += OnLoaded; - } + internal event EventHandler ContextMenuRequested; public void SetFlyout(Uwp.UI.Controls.NotificationFlyout flyout) { @@ -75,6 +69,13 @@ namespace NotificationFlyout.Wpf.UI.Controls } } + protected override void OnContentLoaded() + { + PrepareNotificationIcon(); + PrepareTaskbar(); + UpdateWindow(); + } + protected override void OnDeactivated(EventArgs args) { HideFlyout(); @@ -104,20 +105,10 @@ namespace NotificationFlyout.Wpf.UI.Controls if (args.MouseButton == MouseButton.Right) { - ShowContextMenuFlyout(); + ContextMenuRequested?.Invoke(this, EventArgs.Empty); } } - private void OnLoaded(object sender, RoutedEventArgs args) - { - PrepareNotificationIcon(); - PrepareTaskbar(); - - _isLoaded = true; - - UpdateWindow(); - } - private void OnTaskbarChanged(object sender, EventArgs args) { UpdateWindow(); @@ -130,14 +121,13 @@ namespace NotificationFlyout.Wpf.UI.Controls private void PrepareNotificationIcon() { - _contextMenuXamlHost = new ContextMenuXamlHost(); - _contextMenuXamlHost.Show(); - _notificationIconHelper = NotificationIconHelper.Create(this); _notificationIconHelper.IconInvoked += OnIconInvoked; _systemPersonalisationHelper = SystemPersonalisationHelper.Create(this); _systemPersonalisationHelper.ThemeChanged += OnThemeChanged; + + UpdateIcons(); } private void PrepareTaskbar() @@ -145,10 +135,6 @@ namespace NotificationFlyout.Wpf.UI.Controls _taskbarHelper = TaskbarHelper.Create(this); _taskbarHelper.TaskbarChanged += OnTaskbarChanged; } - private void ShowContextMenuFlyout() - { - _contextMenuXamlHost.ShowContextMenuFlyout(); - } private void UpdateFlyoutContent() { @@ -166,7 +152,7 @@ namespace NotificationFlyout.Wpf.UI.Controls private async void UpdateIcons() { - if (!_isLoaded) return; + if (!IsLoaded) return; if (_flyout == null) return; @@ -185,6 +171,7 @@ namespace NotificationFlyout.Wpf.UI.Controls _notificationIconHelper.SetIcon(icon.Handle); } + private void UpdateRequestedTheme() { if (_flyout == null) return; @@ -200,7 +187,7 @@ namespace NotificationFlyout.Wpf.UI.Controls private void UpdateWindow() { - if (!_isLoaded) return; + if (!IsLoaded) return; var flyoutHost = GetHostContent(); if (flyoutHost == null) return; diff --git a/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/XamlHostWindow.cs b/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/XamlHostWindow.cs index 91b3e96..3e7a24d 100644 --- a/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/XamlHostWindow.cs +++ b/src/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/XamlHostWindow.cs @@ -1,33 +1,44 @@ using Microsoft.Toolkit.Wpf.UI.XamlHost; using NotificationFlyout.Wpf.UI.Extensions; +using System; using System.Windows; using System.Windows.Media; namespace NotificationFlyout.Wpf.UI.Controls { - internal class XamlHostWindow : Window where TXamlContent : class + internal class XamlHostWindow : Window where TXamlContent : Windows.UI.Xaml.UIElement { internal const double WindowSize = 5; + protected new bool IsLoaded; private WindowsXamlHost _xamlHost; - public XamlHostWindow() { PrepareDefaultWindow(); PrepareWindowsXamlHost(); Loaded += OnLoaded; + ContentRendered += OnContentRendered; } - internal TXamlContent GetHostContent() { if (_xamlHost == null) return null; return _xamlHost.GetUwpInternalObject() as TXamlContent; } + protected virtual void OnContentLoaded() + { + } + + private void OnContentRendered(object sender, EventArgs args) + { + IsLoaded = true; + OnContentLoaded(); + } + private void OnLoaded(object sender, RoutedEventArgs args) { - this.Hidden(); + this.Hidden(); } private void PrepareDefaultWindow() @@ -37,7 +48,7 @@ namespace NotificationFlyout.Wpf.UI.Controls WindowStyle = WindowStyle.None; ResizeMode = ResizeMode.NoResize; AllowsTransparency = true; - Background = new SolidColorBrush(Colors.Red); + Background = new SolidColorBrush(Colors.Transparent); Height = WindowSize; Width = WindowSize; } @@ -46,14 +57,13 @@ namespace NotificationFlyout.Wpf.UI.Controls { _xamlHost = new WindowsXamlHost { - InitialTypeName = typeof(TXamlContent).FullName + InitialTypeName = typeof(TXamlContent).FullName, + Height = 0, + Width = 0, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch }; - _xamlHost.Height = 0; - _xamlHost.Width = 0; - _xamlHost.HorizontalAlignment = HorizontalAlignment.Stretch; - _xamlHost.VerticalAlignment = VerticalAlignment.Stretch; - Content = _xamlHost; } }