[WIP] Move over to a popup

This commit is contained in:
Daniel Clark
2021-02-26 14:34:13 +00:00
parent d25744a9f4
commit f0b42d1551
13 changed files with 227 additions and 616 deletions
@@ -1,20 +1,17 @@
using System;
using TheXamlGuy.NotificationFlyout.Shared.UI;
using Windows.Foundation;
using Windows.UI;
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;
namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
{
[ContentProperty(Name = "Content")]
public class NotificationFlyout : DependencyObject
public class NotificationFlyout : ContentControl
{
public static readonly DependencyProperty FlyoutPresenterStyleProperty =
DependencyProperty.Register(nameof(FlyoutPresenterStyle),
typeof(Style), typeof(NotificationFlyout),
new PropertyMetadata(null));
public static readonly DependencyProperty IconSourceProperty =
DependencyProperty.Register(nameof(IconSource),
typeof(ImageSource), typeof(NotificationFlyout),
@@ -25,16 +22,6 @@ namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
typeof(ImageSource), typeof(NotificationFlyout),
new PropertyMetadata(null));
public static readonly DependencyProperty RequestedThemeProperty =
DependencyProperty.Register(nameof(RequestedTheme),
typeof(ElementTheme), typeof(NotificationFlyout),
new PropertyMetadata(ElementTheme.Default));
public static DependencyProperty ContentProperty =
DependencyProperty.Register(nameof(Content),
typeof(UIElement), typeof(NotificationFlyout),
new PropertyMetadata(null));
public static DependencyProperty ContextMenuProperty =
DependencyProperty.Register(nameof(ContextMenu),
typeof(NotificationFlyoutContextMenu), typeof(NotificationFlyout),
@@ -47,6 +34,8 @@ namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
private static INotificationFlyoutApplication _applicationInstance;
private Popup _popup;
public event EventHandler<object> Closed;
public event TypedEventHandler<NotificationFlyout, NotificationFlyoutClosingEventArgs> Closing;
@@ -61,24 +50,12 @@ namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
internal event EventHandler PlacementChanged;
public UIElement Content
{
get => (UIElement)GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
public NotificationFlyoutContextMenu ContextMenu
{
get => (NotificationFlyoutContextMenu)GetValue(ContextMenuProperty);
set => SetValue(ContextMenuProperty, value);
}
public Style FlyoutPresenterStyle
{
get => (Style)GetValue(FlyoutPresenterStyleProperty);
set => SetValue(FlyoutPresenterStyleProperty, value);
}
public ImageSource IconSource
{
get => (ImageSource)GetValue(IconSourceProperty);
@@ -97,12 +74,6 @@ namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
set => SetValue(PlacementProperty, value);
}
public ElementTheme RequestedTheme
{
get => (ElementTheme)GetValue(RequestedThemeProperty);
set => SetValue(RequestedThemeProperty, value);
}
public static INotificationFlyoutApplication GetApplication() => _applicationInstance;
internal static void SetApplication(INotificationFlyoutApplication application) => _applicationInstance = application;
@@ -115,6 +86,51 @@ namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
internal void InvokeOpeningEvent(object obj) => Opening?.Invoke(this, obj);
internal void SetPlacement(double horizontalOffset, double verticalOffset, NotificationFlyoutTaskbarPlacement flyoutTaskbarPlacement)
{
if (_popup == null)
{
PreparePopup();
}
var width = 100;
var height = 100;
var desiredHorizontalOffset = horizontalOffset;
var desiredVerticalOffset = verticalOffset;
switch (flyoutTaskbarPlacement)
{
case NotificationFlyoutTaskbarPlacement.Left:
desiredVerticalOffset = 0;
break;
case NotificationFlyoutTaskbarPlacement.Top:
desiredHorizontalOffset -= width;
break;
case NotificationFlyoutTaskbarPlacement.Right:
desiredHorizontalOffset -= width;
desiredVerticalOffset -= height;
break;
case NotificationFlyoutTaskbarPlacement.Bottom:
desiredHorizontalOffset -= width;
desiredVerticalOffset -= height;
break;
}
_popup.HorizontalOffset = desiredHorizontalOffset;
_popup.VerticalOffset = desiredVerticalOffset;
}
internal void Show()
{
if (_popup == null)
{
PreparePopup();
}
_popup.IsOpen = true;
}
private static void OnContextMenuPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var sender = dependencyObject as NotificationFlyout;
@@ -138,5 +154,20 @@ namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
private void OnIconPropertyChanged() => IconSourceChanged?.Invoke(this, EventArgs.Empty);
private void OnPlacementPropertyChanged() => PlacementChanged?.Invoke(this, EventArgs.Empty);
private void PreparePopup()
{
var f = new Grid { Background = new SolidColorBrush(Colors.Blue), Height = 100, Width = 100 };
f.Children.Add(new Button { Content = "hefrefsef2" });
_popup = new Popup
{
XamlRoot = XamlRoot,
ShouldConstrainToRootBounds = false,
HorizontalOffset = -1,
VerticalOffset = -1,
Child = f
};
}
}
}
@@ -28,13 +28,13 @@
FallbackColor="{ThemeResource SystemAccentColorDark1}"
TintColor="{ThemeResource SystemAccentColorDark1}"
TintOpacity="0.8" />
<Style TargetType="controls:NotificationFlyoutPresenter">
<Style TargetType="controls:NotificationFlyout">
<Setter Property="Background" Value="{ThemeResource NotificationFlyoutPresenterBackgroundBrush}" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:NotificationFlyoutPresenter">
<ControlTemplate TargetType="controls:NotificationFlyout">
<Grid
x:Name="Root"
Padding="{TemplateBinding Padding}"
@@ -1,11 +1,10 @@
namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
{
internal enum NotificationFlyoutPresenterPlacement
internal enum NotificationFlyoutTaskbarPlacement
{
Left,
Top,
Right,
Bottom,
FullRight
}
}
@@ -1,26 +0,0 @@
using System.Numerics;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
{
public class NotificationFlyoutPresenter : ContentControl
{
public NotificationFlyoutPresenter() => DefaultStyleKey = typeof(NotificationFlyoutPresenter);
protected override void OnApplyTemplate()
{
if (GetTemplateChild("Root") is Grid contentRoot)
{
contentRoot.Shadow = new ThemeShadow();
var currentTranslation = contentRoot.Translation;
var translation = new Vector3(currentTranslation.X, currentTranslation.Y, 16.0f);
contentRoot.Translation = translation;
}
}
internal void UpdateThemeVisualState(bool isColorPrevalence) => VisualStateManager.GoToState(this, isColorPrevalence ? "ColorPrevalenceTheme" : "DefaultTheme", true);
}
}
@@ -1,135 +0,0 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
namespace TheXamlGuy.NotificationFlyout.Uwp.UI.Controls
{
internal class NotificationFlyoutPresenterHost : Control
{
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register(nameof(Content),
typeof(UIElement), typeof(NotificationFlyoutPresenterHost),
new PropertyMetadata(null));
public static readonly DependencyProperty FlyoutPresenterStyleProperty =
DependencyProperty.Register(nameof(FlyoutPresenterStyle),
typeof(Style), typeof(NotificationFlyoutPresenterHost),
new PropertyMetadata(null));
private Flyout _flyout;
private NotificationFlyout _notificationFlyout;
private NotificationFlyoutPresenter _notificationFlyoutPresenter;
private Grid _root;
public NotificationFlyoutPresenterHost() => DefaultStyleKey = typeof(NotificationFlyoutPresenterHost);
public UIElement Content
{
get => (UIElement)GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
public Style FlyoutPresenterStyle
{
get => (Style)GetValue(FlyoutPresenterStyleProperty);
set => SetValue(FlyoutPresenterStyleProperty, value);
}
public void HideFlyout()
{
if (_root == null) return;
FlyoutBase flyout = FlyoutBase.GetAttachedFlyout(_root);
flyout.Hide();
}
public void UpdatePlacement(NotificationFlyoutPresenterPlacement placement)
{
var state = placement.ToString();
VisualStateManager.GoToState(this, state, true);
VisualStateManager.GoToState(_notificationFlyoutPresenter, state, true);
}
public void SetOwningFlyout(NotificationFlyout flyout)
{
_notificationFlyout = flyout;
BindingOperations.SetBinding(this, ContentProperty,
new Binding
{
Source = _notificationFlyout,
Path =
new PropertyPath(nameof(Content)),
Mode = BindingMode.TwoWay
});
BindingOperations.SetBinding(this, RequestedThemeProperty,
new Binding
{
Source = _notificationFlyout,
Path = new PropertyPath(nameof(RequestedTheme)),
Mode = BindingMode.TwoWay
});
BindingOperations.SetBinding(this, FlyoutPresenterStyleProperty,
new Binding
{
Source = _notificationFlyout,
Path = new PropertyPath(nameof(FlyoutPresenterStyle)),
Mode = BindingMode.TwoWay
});
}
public void ShowFlyout(FlyoutPlacementMode placementMode)
{
if (_root == null) return;
var flyout = FlyoutBase.GetAttachedFlyout(_root);
flyout.ShowAt(_root, new FlyoutShowOptions
{
Placement = placementMode,
ShowMode = FlyoutShowMode.Transient,
});
}
public void UpdateTheme(bool isColorPrevalence)
{
if (_notificationFlyoutPresenter == null) return;
_notificationFlyoutPresenter.UpdateThemeVisualState(isColorPrevalence);
}
protected override void OnApplyTemplate()
{
_notificationFlyoutPresenter = GetTemplateChild("NotificationFlyoutPresenter") as NotificationFlyoutPresenter;
if (_notificationFlyoutPresenter != null)
{
_notificationFlyoutPresenter.ApplyTemplate();
}
_flyout = GetTemplateChild("Flyout") as Flyout;
if (_flyout != null)
{
_flyout.Closing -= OnFlyoutClosing;
_flyout.Closed -= OnFlyoutClosed;
_flyout.Opening -= OnFlyoutOpening;
_flyout.Opened -= OnFlyoutOpened;
_flyout.Closing += OnFlyoutClosing;
_flyout.Closed += OnFlyoutClosed;
_flyout.Opening += OnFlyoutOpening;
_flyout.Opened += OnFlyoutOpened;
}
_root = GetTemplateChild("Root") as Grid;
}
private void OnFlyoutClosed(object sender, object args) => _notificationFlyout?.InvokeClosedEvent(args);
private void OnFlyoutClosing(FlyoutBase sender, FlyoutBaseClosingEventArgs args) => _notificationFlyout?.InvokeClosingEvent(new NotificationFlyoutClosingEventArgs());
private void OnFlyoutOpened(object sender, object args) => _notificationFlyout?.InvokeOpenedEvent(args);
private void OnFlyoutOpening(object sender, object args) => _notificationFlyout?.InvokeOpeningEvent(args);
}
}
@@ -1,99 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:TheXamlGuy.NotificationFlyout.Uwp.UI.Controls">
<Style TargetType="controls:NotificationFlyoutPresenterHost">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:NotificationFlyoutPresenterHost">
<Grid x:Name="Root">
<Grid.Resources>
<Style x:Key="DefaultFlyoutPresenterStyle" TargetType="FlyoutPresenter">
<Setter Property="IsDefaultShadowEnabled" Value="False" />
<Setter Property="MaxWidth" Value="Auto" />
<Setter Property="MaxHeight" Value="Auto" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="FlyoutPresenter">
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style
x:Key="TopFlyoutPresenterStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Setter Property="Margin" Value="0,-7,0,0" />
</Style>
<Style
x:Key="BottomFlyoutPresenterStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Setter Property="Margin" Value="0,7,0,0" />
</Style>
<Style
x:Key="LeftFlyoutPresenterStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Setter Property="Margin" Value="-7,0,0,0" />
</Style>
<Style
x:Key="RightFlyoutPresenterStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Setter Property="Margin" Value="7,0,0,0" />
</Style>
</Grid.Resources>
<FlyoutBase.AttachedFlyout>
<Flyout
x:Name="Flyout"
AreOpenCloseAnimationsEnabled="False"
FlyoutPresenterStyle="{StaticResource BottomFlyoutPresenterStyle}"
ShouldConstrainToRootBounds="False">
<controls:NotificationFlyoutPresenter
x:Name="NotificationFlyoutPresenter"
Content="{TemplateBinding Content}"
Style="{TemplateBinding FlyoutPresenterStyle}" />
</Flyout>
</FlyoutBase.AttachedFlyout>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PlacementStates">
<VisualState x:Name="Bottom">
<VisualState.Setters>
<Setter Target="Flyout.FlyoutPresenterStyle" Value="{StaticResource BottomFlyoutPresenterStyle}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Top">
<VisualState.Setters>
<Setter Target="Flyout.FlyoutPresenterStyle" Value="{StaticResource TopFlyoutPresenterStyle}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Left">
<VisualState.Setters>
<Setter Target="Flyout.FlyoutPresenterStyle" Value="{StaticResource LeftFlyoutPresenterStyle}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Right">
<VisualState.Setters>
<Setter Target="Flyout.FlyoutPresenterStyle" Value="{StaticResource RightFlyoutPresenterStyle}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="FullRight">
<VisualState.Setters>
<Setter Target="Flyout.FlyoutPresenterStyle" Value="{StaticResource BottomFlyoutPresenterStyle}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -18,11 +18,7 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="NotificationFlyoutPresenter\NotificationFlyoutPresenterHost.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="NotificationFlyoutPresenter\NotificationFlyoutPresenter.xaml">
<Page Include="NotificationFlyout\NotificationFlyout.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -1,7 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///TheXamlGuy.NotificationFlyout.Uwp.UI.Controls/NotificationFlyoutPresenter/NotificationFlyoutPresenter.xaml" />
<ResourceDictionary Source="ms-appx:///TheXamlGuy.NotificationFlyout.Uwp.UI.Controls/NotificationFlyoutPresenter/NotificationFlyoutPresenterHost.xaml" />
<ResourceDictionary Source="ms-appx:///TheXamlGuy.NotificationFlyout.Uwp.UI.Controls/NotificationFlyoutPresenter/NotificationFlyoutContextMenuFlyoutHost.xaml" />
<ResourceDictionary Source="ms-appx:///TheXamlGuy.NotificationFlyout.Uwp.UI.Controls/NotificationFlyout/NotificationFlyout.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
@@ -1,62 +1,188 @@
using TheXamlGuy.NotificationFlyout.Common.Helpers;
using System.Windows;
using System.Windows.Markup;
using TheXamlGuy.NotificationFlyout.Shared.UI;
using System;
using Windows.UI.Xaml.Controls;
using TheXamlGuy.NotificationFlyout.Uwp.UI.Controls;
using TheXamlGuy.NotificationFlyout.Wpf.UI.Extensions;
using System.Windows.Media.Imaging;
using TheXamlGuy.NotificationFlyout.Common.Extensions;
namespace TheXamlGuy.NotificationFlyout.Wpf.UI.Controls
{
[ContentProperty(nameof(Flyout))]
public class NotificationFlyoutApplication : DependencyObject, INotificationFlyoutApplication
public class NotificationFlyoutApplication : DependencyObject
{
public static DependencyProperty FlyoutProperty =
DependencyProperty.Register(nameof(Flyout),
typeof(Uwp.UI.Controls.NotificationFlyout), typeof(NotificationFlyoutApplication),
new PropertyMetadata(null, OnFlyoutPropertyChanged));
private static NotificationFlyoutApplication _application;
private NotificationFlyoutXamlHost _notificationFlyoutXamlHost;
private const string ShellTrayHandleName = "Shell_TrayWnd";
private TransparentXamlHost<ContentControl> _notificationFlyoutXamlHost;
private NotificationIconHelper _notificationIconHelper;
private SystemPersonalisationHelper _systemPersonalisationHelper;
private TaskbarHelper _taskbarHelper;
public NotificationFlyoutApplication()
{
_application = this;
Uwp.UI.Controls.NotificationFlyout.SetApplication(this);
_notificationIconHelper = NotificationIconHelper.Create();
_notificationIconHelper.IconInvoked += OnIconInvoked;
_taskbarHelper = TaskbarHelper.Create();
_taskbarHelper.TaskbarChanged += OnTaskbarChanged;
_systemPersonalisationHelper = SystemPersonalisationHelper.Current;
_systemPersonalisationHelper.ThemeChanged += OnThemeChanged;
WndProcListener.Current.Start();
PrepareFlyoutHost();
WndProcListener.Current.Start();
}
public static INotificationFlyoutApplication Current => _application;
public Uwp.UI.Controls.NotificationFlyout Flyout
{
get => (Uwp.UI.Controls.NotificationFlyout)GetValue(FlyoutProperty);
set => SetValue(FlyoutProperty, value);
}
public void Exit() => _notificationFlyoutXamlHost.Close();
public void HideFlyout() => _notificationFlyoutXamlHost.HideFlyout();
public void OpenAsWindow<TUIElement>() where TUIElement : Windows.UI.Xaml.UIElement
{
var window = new XamlHost<TUIElement>();
window.Show();
}
public void ShowFlyout() => _notificationFlyoutXamlHost.ShowFlyout();
private static void OnFlyoutPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var sender = dependencyObject as NotificationFlyoutApplication;
sender?.OnFlyoutPropertyChanged();
}
private void OnFlyoutPropertyChanged() => _notificationFlyoutXamlHost.SetOwningFlyout(Flyout);
private void OnFlyoutPropertyChanged() => PrepareFlyout();
private void PrepareFlyout()
{
if (Flyout == null) return;
Flyout.IconSourceChanged += OnIconSourceChanged;
var content = _notificationFlyoutXamlHost.GetHostContent();
if (content != null)
{
content.Content = Flyout;
}
UpdateIcons();
}
private void OnIconInvoked(object sender, NotificationIconInvokedEventArgs args)
{
if (args.PointerButton == PointerButton.Left)
{
Flyout.Show();
}
if (args.PointerButton == PointerButton.Right)
{
}
}
private void OnIconSourceChanged(object sender, EventArgs args) => UpdateIcons();
private void OnNotificationFlyoutXamlHostClosed(object sender, EventArgs args)
{
_notificationIconHelper?.Dispose();
}
private void OnTaskbarChanged(object sender, EventArgs args) => SetFlyoutPlacement();
private void OnThemeChanged(object sender, SystemPersonalisationChangedEventArgs args)
{
}
private void PrepareFlyoutHost()
{
_notificationFlyoutXamlHost = new NotificationFlyoutXamlHost();
_notificationFlyoutXamlHost = new TransparentXamlHost<ContentControl>();
_notificationFlyoutXamlHost.Closed += OnNotificationFlyoutXamlHostClosed;
var taskbarState = _taskbarHelper.GetCurrentState();
_notificationFlyoutXamlHost.Left = taskbarState.Screen.WorkingArea.Left;
_notificationFlyoutXamlHost.Top = taskbarState.Screen.WorkingArea.Top;
_notificationFlyoutXamlHost.Show();
}
private void SetFlyoutPlacement()
{
var taskbarState = _taskbarHelper.GetCurrentState();
_notificationFlyoutXamlHost.Left = 0;
_notificationFlyoutXamlHost.Top = 0;
double left;
double top;
var dpiX = _notificationFlyoutXamlHost.DpiX();
var dpiY = _notificationFlyoutXamlHost.DpiY();
NotificationFlyoutTaskbarPlacement flyoutTaskBarPlacement;
switch (taskbarState.Placement)
{
case TaskbarPlacement.Left:
flyoutTaskBarPlacement = NotificationFlyoutTaskbarPlacement.Left;
top = taskbarState.Rect.Bottom / dpiX;
left = taskbarState.Rect.Right / dpiY;
break;
case TaskbarPlacement.Top:
flyoutTaskBarPlacement = NotificationFlyoutTaskbarPlacement.Top;
top = taskbarState.Rect.Bottom / dpiX;
left = (_notificationFlyoutXamlHost.FlowDirection == FlowDirection.RightToLeft ? taskbarState.Rect.Left : taskbarState.Rect.Right) / dpiY;
break;
case TaskbarPlacement.Right:
flyoutTaskBarPlacement = NotificationFlyoutTaskbarPlacement.Right;
top = taskbarState.Rect.Bottom / dpiX;
left = taskbarState.Rect.Left / dpiY;
break;
case TaskbarPlacement.Bottom:
flyoutTaskBarPlacement = NotificationFlyoutTaskbarPlacement.Bottom;
top = taskbarState.Rect.Top / dpiX;
left = (_notificationFlyoutXamlHost.FlowDirection == FlowDirection.RightToLeft ? taskbarState.Rect.Left : taskbarState.Rect.Right) / dpiY;
break;
default:
throw new ArgumentOutOfRangeException();
}
Flyout.SetPlacement(left, top, flyoutTaskBarPlacement);
}
private async void UpdateIcons()
{
if (Flyout == null) return;
var shellTrayHandle = WindowHelper.GetHandle(ShellTrayHandleName);
if (shellTrayHandle == null) return;
var dpi = WindowHelper.GetDpi(shellTrayHandle);
var desiredIconSource = _systemPersonalisationHelper.Theme == SystemTheme.Dark ? Flyout.IconSource : Flyout.LightIconSource;
if (desiredIconSource == null)
{
var fallbackIconSource = new BitmapImage(new Uri($"pack://application:,,,/{GetType().Namespace};component/Assets/notification-icon-{(_systemPersonalisationHelper.Theme == SystemTheme.Dark ? "default" : "light")}.ico"));
using var icon = fallbackIconSource.ConvertToIcon(dpi);
_notificationIconHelper.SetIcon(icon.Handle);
}
else
{
using var icon = await desiredIconSource.ConvertToIconAsync(dpi);
_notificationIconHelper.SetIcon(icon.Handle);
}
}
private void UpdateTheme()
{
//var content = GetHostContent();
//if (content != null)
//{
// content.UpdateTheme(_systemPersonalisationHelper.IsColorPrevalence);
//}
}
}
}
@@ -1,282 +0,0 @@
using TheXamlGuy.NotificationFlyout.Common.Extensions;
using TheXamlGuy.NotificationFlyout.Common.Helpers;
using TheXamlGuy.NotificationFlyout.Uwp.UI.Controls;
using TheXamlGuy.NotificationFlyout.Wpf.UI.Extensions;
using System;
using System.Windows;
using Windows.UI.Xaml.Controls.Primitives;
using System.Windows.Media.Imaging;
namespace TheXamlGuy.NotificationFlyout.Wpf.UI.Controls
{
internal class NotificationFlyoutXamlHost : TransparentXamlHost<NotificationFlyoutPresenterHost>
{
private const string ShellTrayHandleName = "Shell_TrayWnd";
private NotificationFlyoutContextMenuXamlHost _contextMenuXamlHost;
private Uwp.UI.Controls.NotificationFlyout _flyout;
private NotificationIconHelper _notificationIconHelper;
private SystemPersonalisationHelper _systemPersonalisationHelper;
private TaskbarHelper _taskbarHelper;
internal void HideFlyout()
{
var flyoutHost = GetHostContent();
if (flyoutHost != null)
{
flyoutHost.HideFlyout();
}
}
internal void SetOwningFlyout(Uwp.UI.Controls.NotificationFlyout flyout)
{
if (_flyout != null)
{
_flyout.IconSourceChanged -= OnIconSourceChanged;
_flyout.ContextMenuChanged -= OnContextMenuChanged;
_flyout.PlacementChanged -= OnPlacementChanged;
}
_flyout = flyout;
_flyout.IconSourceChanged += OnIconSourceChanged;
_flyout.ContextMenuChanged += OnContextMenuChanged;
_flyout.PlacementChanged += OnPlacementChanged;
var content = GetHostContent();
if (content != null)
{
content.SetOwningFlyout(_flyout);
}
UpdateIcons();
UpdateContextMenu();
}
internal void ShowFlyout()
{
var content = GetHostContent();
if (content != null)
{
var taskbarState = _taskbarHelper.GetCurrentState();
var flyoutPlacement = FlyoutPlacementMode.Top;
switch (_flyout.Placement)
{
case NotificationFlyoutPlacement.Auto:
flyoutPlacement = taskbarState.Placement switch
{
TaskbarPlacement.Left => FlyoutPlacementMode.Right,
TaskbarPlacement.Top => FlyoutPlacementMode.Bottom,
TaskbarPlacement.Right => FlyoutPlacementMode.Left,
TaskbarPlacement.Bottom => FlyoutPlacementMode.Top,
_ => throw new ArgumentOutOfRangeException(),
};
break;
}
Activate();
content.ShowFlyout(flyoutPlacement);
}
}
protected override void OnClosed(EventArgs args)
{
_notificationIconHelper.Dispose();
if (_contextMenuXamlHost == null) return;
_contextMenuXamlHost.Close();
}
protected override void OnContentLoaded()
{
PrepareNotificationIcon();
PrepareTaskbar();
UpdatePlacement();
UpdateTheme();
}
protected override void OnDeactivated(EventArgs args) => HideFlyout();
private void OnContextMenuChanged(object sender, EventArgs args) => UpdateContextMenu();
private void OnIconInvoked(object sender, NotificationIconInvokedEventArgs args)
{
if (args.PointerButton == PointerButton.Left)
{
ShowFlyout();
}
if (args.PointerButton == PointerButton.Right)
{
ShowContextMenuFlyout();
}
}
private void OnIconSourceChanged(object sender, EventArgs args) => UpdateIcons();
private void OnPlacementChanged(object sender, EventArgs args) => UpdatePlacement();
private void OnTaskbarChanged(object sender, EventArgs args) => UpdatePlacement();
private void OnThemeChanged(object sender, SystemPersonalisationChangedEventArgs args)
{
UpdateTheme();
UpdateIcons();
}
private void UpdateContextMenu()
{
if (_contextMenuXamlHost != null)
{
_contextMenuXamlHost.Close();
_contextMenuXamlHost = null;
}
var contextMenu = _flyout.ContextMenu;
if (contextMenu == null) return;
if (_contextMenuXamlHost == null)
{
_contextMenuXamlHost = new NotificationFlyoutContextMenuXamlHost();
_contextMenuXamlHost.Show();
}
_contextMenuXamlHost.SetOwningFlyout(_flyout);
}
private void PrepareNotificationIcon()
{
_notificationIconHelper = NotificationIconHelper.Create();
_notificationIconHelper.IconInvoked += OnIconInvoked;
_systemPersonalisationHelper = SystemPersonalisationHelper.Current;
_systemPersonalisationHelper.ThemeChanged += OnThemeChanged;
UpdateIcons();
}
private void PrepareTaskbar()
{
_taskbarHelper = TaskbarHelper.Create();
_taskbarHelper.TaskbarChanged += OnTaskbarChanged;
}
private void ShowContextMenuFlyout()
{
if (_contextMenuXamlHost == null) return;
_contextMenuXamlHost.ShowContextMenuFlyout();
}
private async void UpdateIcons()
{
if (!IsLoaded) return;
if (_flyout == null) return;
var shellTrayHandle = WindowHelper.GetHandle(ShellTrayHandleName);
if (shellTrayHandle == null) return;
var dpi = WindowHelper.GetDpi(shellTrayHandle);
var desiredIconSource = _systemPersonalisationHelper.Theme == SystemTheme.Dark ? _flyout.IconSource : _flyout.LightIconSource;
if (desiredIconSource == null)
{
var fallbackIconSource = new BitmapImage(new Uri($"pack://application:,,,/{GetType().Namespace};component/Assets/notification-icon-{(_systemPersonalisationHelper.Theme == SystemTheme.Dark ? "default" : "light")}.ico"));
using var icon = fallbackIconSource.ConvertToIcon(dpi);
_notificationIconHelper.SetIcon(icon.Handle);
}
else
{
using var icon = await desiredIconSource.ConvertToIconAsync(dpi);
_notificationIconHelper.SetIcon(icon.Handle);
}
}
private void UpdateTheme()
{
var content = GetHostContent();
if (content != null)
{
content.UpdateTheme(_systemPersonalisationHelper.IsColorPrevalence);
}
}
private void UpdatePlacement()
{
if (!IsLoaded) return;
var flyoutHost = GetHostContent();
if (flyoutHost == null) return;
var taskbarState = _taskbarHelper.GetCurrentState();
Left = taskbarState.Screen.WorkingArea.Left;
Top = taskbarState.Screen.WorkingArea.Top;
var width = WindowSize * this.DpiX();
var height = WindowSize * this.DpiY();
double top = 0, left = 0;
var taskbarRect = taskbarState.Rect;
var taskBarPlacement = taskbarState.Placement;
var presenterPlacement = NotificationFlyoutPresenterPlacement.Bottom;
switch (_flyout.Placement)
{
case NotificationFlyoutPlacement.Auto:
switch (taskBarPlacement)
{
case TaskbarPlacement.Left:
presenterPlacement = NotificationFlyoutPresenterPlacement.Left;
top = taskbarRect.Bottom - height;
left = taskbarRect.Right;
break;
case TaskbarPlacement.Top:
presenterPlacement = NotificationFlyoutPresenterPlacement.Top;
top = taskbarRect.Bottom;
left = FlowDirection == FlowDirection.RightToLeft ? taskbarRect.Left : taskbarRect.Right - width;
break;
case TaskbarPlacement.Right:
presenterPlacement = NotificationFlyoutPresenterPlacement.Right;
top = taskbarRect.Bottom - height;
left = taskbarRect.Left - width;
break;
case TaskbarPlacement.Bottom:
presenterPlacement = NotificationFlyoutPresenterPlacement.Bottom;
top = taskbarRect.Top - height;
left = FlowDirection == FlowDirection.RightToLeft ? taskbarRect.Left : taskbarRect.Right - width;
break;
default:
throw new ArgumentOutOfRangeException();
}
break;
case NotificationFlyoutPlacement.Right:
presenterPlacement = NotificationFlyoutPresenterPlacement.FullRight;
switch (taskBarPlacement)
{
case TaskbarPlacement.Left:
case TaskbarPlacement.Top:
case TaskbarPlacement.Right:
left = taskbarState.Screen.Bounds.Width - width;
top = taskbarState.Screen.Bounds.Height - height;
break;
case TaskbarPlacement.Bottom:
top = taskbarRect.Top - height;
left = FlowDirection == FlowDirection.RightToLeft ? taskbarRect.Left : taskbarRect.Right - width;
break;
default:
throw new ArgumentOutOfRangeException();
}
break;
}
this.SetWindowPosition(top, left, height, width);
flyoutHost.UpdatePlacement(presenterPlacement);
}
}
}
@@ -7,7 +7,7 @@ namespace TheXamlGuy.NotificationFlyout.Wpf.UI.Controls
{
internal class TransparentXamlHost<TXamlContent> : XamlHost<TXamlContent> where TXamlContent : Windows.UI.Xaml.UIElement
{
internal const double WindowSize = 5;
internal const double WindowSize = 0;
public TransparentXamlHost()
{