diff --git a/NotificationFlyout.Shared.UI/Helpers/SystemPersonalisationHelper.cs b/NotificationFlyout.Shared.UI/Helpers/SystemPersonalisationHelper.cs new file mode 100644 index 0000000..a683afe --- /dev/null +++ b/NotificationFlyout.Shared.UI/Helpers/SystemPersonalisationHelper.cs @@ -0,0 +1,35 @@ +using System; +using Windows.UI.ViewManagement; + +namespace NotificationFlyout.Shared.UI.Helpers +{ + public class SystemPersonalisationHelper + { + private readonly UISettings _settings = new UISettings(); + + private SystemPersonalisationHelper() + { + _settings.ColorValuesChanged += _settings_ColorValuesChanged; + } + + private void _settings_ColorValuesChanged(UISettings sender, object args) + { + ThemeChanged?.Invoke(this, EventArgs.Empty); + } + + public event EventHandler ThemeChanged; + + public SystemTheme SystemTheme => GetSystemTheme(); + + public static SystemPersonalisationHelper Create() + { + return new SystemPersonalisationHelper(); + } + + private SystemTheme GetSystemTheme() + { + var uiTheme = _settings.GetColorValue(UIColorType.Background).ToString(); + return uiTheme == "#FFFFFFFF" ? SystemTheme.Light : SystemTheme.Dark; + } + } +} diff --git a/NotificationFlyout.Shared.UI/Helpers/SystemTheme.cs b/NotificationFlyout.Shared.UI/Helpers/SystemTheme.cs new file mode 100644 index 0000000..ee90015 --- /dev/null +++ b/NotificationFlyout.Shared.UI/Helpers/SystemTheme.cs @@ -0,0 +1,8 @@ +namespace NotificationFlyout.Shared.UI.Helpers +{ + public enum SystemTheme + { + Dark, + Light, + } +} diff --git a/NotificationFlyout.Shared.UI/NotificationFlyout.Shared.UI.csproj b/NotificationFlyout.Shared.UI/NotificationFlyout.Shared.UI.csproj new file mode 100644 index 0000000..92145d4 --- /dev/null +++ b/NotificationFlyout.Shared.UI/NotificationFlyout.Shared.UI.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp3.1 + + + + + + + diff --git a/NotificationFlyout.Tray/Assets/Icon-Light.ico b/NotificationFlyout.Tray/Assets/Icon-Light.ico new file mode 100644 index 0000000..5c7e7a8 Binary files /dev/null and b/NotificationFlyout.Tray/Assets/Icon-Light.ico differ diff --git a/NotificationFlyout.Tray/NotificationFlyout.Sample.Wpf.csproj b/NotificationFlyout.Tray/NotificationFlyout.Sample.Wpf.csproj index 16b2061..b7af113 100644 --- a/NotificationFlyout.Tray/NotificationFlyout.Sample.Wpf.csproj +++ b/NotificationFlyout.Tray/NotificationFlyout.Sample.Wpf.csproj @@ -12,6 +12,7 @@ + @@ -27,6 +28,7 @@ + diff --git a/NotificationFlyout.Tray/SampleNotificationFlyout.xaml b/NotificationFlyout.Tray/SampleNotificationFlyout.xaml index 0b4c2e5..947fc42 100644 --- a/NotificationFlyout.Tray/SampleNotificationFlyout.xaml +++ b/NotificationFlyout.Tray/SampleNotificationFlyout.xaml @@ -4,6 +4,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sample="clr-namespace:NotificationFlyout.Sample;assembly=NotificationFlyout.Sample" IconSource="/Assets/Icon.ico" - LightIconSource="/Assets/Icon.ico"> + LightIconSource="/Assets/Icon-Light.ico"> diff --git a/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyout.cs b/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyout.cs index 1c08447..fe991bc 100644 --- a/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyout.cs +++ b/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyout.cs @@ -1,6 +1,4 @@ using NotificationFlyout.Uwp.UI.Controls; -using NotificationFlyout.Wpf.UI.Extensions; -using NotificationFlyout.Wpf.UI.Helpers; using System.Windows; using System.Windows.Markup; using System.Windows.Media; @@ -25,8 +23,6 @@ namespace NotificationFlyout.Wpf.UI.Controls typeof(NotificationFlyoutPresenter), typeof(NotificationFlyout), new PropertyMetadata(null, OnFlyoutPresenterPropertyChanged)); - private const string ShellTrayHandleName = "Shell_TrayWnd"; - private readonly NotificationFlyoutXamlHost _xamlHost; public NotificationFlyout() @@ -82,21 +78,7 @@ namespace NotificationFlyout.Wpf.UI.Controls private void OnIconPropertyChanged() { - SetIcon(); - } - - private void SetIcon() - { - var shellTrayHandle = WindowHelper.GetHandle(ShellTrayHandleName); - if (shellTrayHandle == null) return; - - var dpi = WindowHelper.GetDpi(shellTrayHandle); - - var iconSource = SystemSettingsHelper.DefaultSystemTheme == SystemTheme.Dark ? IconSource : LightIconSource; - if (iconSource == null) return; - - using var icon = iconSource.ConvertToIcon(dpi); - _xamlHost.SetNotificationIcon(icon.Handle); + _xamlHost.SetIcons(IconSource, LightIconSource); } } } \ No newline at end of file diff --git a/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutXamlHost.cs b/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutXamlHost.cs index 6740adf..493d610 100644 --- a/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutXamlHost.cs +++ b/NotificationFlyout.Wpf.UI.Controls/NotificationFlyout/NotificationFlyoutXamlHost.cs @@ -11,10 +11,16 @@ namespace NotificationFlyout.Wpf.UI.Controls { internal class NotificationFlyoutXamlHost : Window { - private const double MaximumOffset = 80; + private const string ShellTrayHandleName = "Shell_TrayWnd"; + private const double WindowSize = 5; + + private ImageSource _defaultIconSource; + private ImageSource _lightIconSource; private NotificationIconHelper _notificationIconHelper; + private SystemPersonalisationHelper _systemPersonalisationHelper; private TaskbarHelper _taskbarHelper; private WindowsXamlHost _xamlHost; + private bool _isLoaded; public NotificationFlyoutXamlHost() { @@ -42,9 +48,12 @@ namespace NotificationFlyout.Wpf.UI.Controls } } - internal void SetNotificationIcon(IntPtr handle) + internal void SetIcons(ImageSource defaultIconSource, ImageSource lightIconSource) { - _notificationIconHelper.SetIcon(handle); + _defaultIconSource = defaultIconSource; + _lightIconSource = lightIconSource; + + UpdateIcon(); } internal void ShowFlyout() @@ -83,7 +92,16 @@ namespace NotificationFlyout.Wpf.UI.Controls PrepareNotificationIcon(); PrepareTaskbar(); + _isLoaded = true; + UpdateWindow(); + UpdateIcon(); + + } + + private void OnSystemThemeChanged(object sender, EventArgs args) + { + UpdateIcon(); } private void OnTaskbarChanged(object sender, EventArgs args) @@ -99,14 +117,17 @@ namespace NotificationFlyout.Wpf.UI.Controls ResizeMode = ResizeMode.NoResize; AllowsTransparency = true; Background = new SolidColorBrush(Colors.Transparent); - Height = 5; - Width = 5; + Height = WindowSize; + Width = WindowSize; } private void PrepareNotificationIcon() { _notificationIconHelper = NotificationIconHelper.Create(this); _notificationIconHelper.IconInvoked += OnIconInvoked; + + _systemPersonalisationHelper = SystemPersonalisationHelper.Create(this); + _systemPersonalisationHelper.ThemeChanged += OnSystemThemeChanged; } private void PrepareTaskbar() @@ -128,8 +149,26 @@ namespace NotificationFlyout.Wpf.UI.Controls Content = _xamlHost; } + private void UpdateIcon() + { + if (!_isLoaded) return; + + var shellTrayHandle = WindowHelper.GetHandle(ShellTrayHandleName); + if (shellTrayHandle == null) return; + + var dpi = WindowHelper.GetDpi(shellTrayHandle); + + var iconSource = _systemPersonalisationHelper.SystemTheme == SystemTheme.Dark ? _defaultIconSource : _lightIconSource; + if (iconSource == null) return; + + using var icon = iconSource.ConvertToIcon(dpi); + _notificationIconHelper.SetIcon(icon.Handle); + } + private void UpdateWindow() { + if (!_isLoaded) return; + var flyoutHost = GetFlyoutHost(); if (flyoutHost == null) return; @@ -138,8 +177,8 @@ namespace NotificationFlyout.Wpf.UI.Controls Left = taskbarState.Screen.WorkingArea.Left; Top = taskbarState.Screen.WorkingArea.Top; - var windowWidth = 5 * this.DpiX(); - var windowHeight = 5 * this.DpiY(); + var windowWidth = WindowSize * this.DpiX(); + var windowHeight = WindowSize * this.DpiY(); double top, left, height, width; diff --git a/NotificationFlyout.Wpf.UI/Helpers/NotificationIconHelper.cs b/NotificationFlyout.Wpf.UI/Helpers/NotificationIconHelper.cs index 0bce1fd..c5597f3 100644 --- a/NotificationFlyout.Wpf.UI/Helpers/NotificationIconHelper.cs +++ b/NotificationFlyout.Wpf.UI/Helpers/NotificationIconHelper.cs @@ -12,9 +12,6 @@ namespace NotificationFlyout.Wpf.UI.Helpers private const int CallbackMessage = 0x400; private const uint IconVersion = 0x4; - private const int WM_LBUTTONUP = 0x0202; - private const int WM_MBUTTONUP = 0x0208; - private const int WM_RBUTTONUP = 0x0205; private readonly object _lock = new(); private readonly IntPtr _windowHandle; private bool _isDisposed; @@ -134,36 +131,29 @@ namespace NotificationFlyout.Wpf.UI.Helpers } } + private void InvokeIconInvoked(MouseButton mouseButton) + { + IconInvoked?.Invoke(this, new NotificationIconInvokedEventArgs { MouseButton = mouseButton }); + } + private void RemoveNotificationIcon() => WriteNotifyIconData(NotifyIconCommand.Delete, NotifyIconDataMember.Message); + private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == CallbackMessage) { - var mouseButton = MouseButton.Left; - var isInvoked = false; - switch ((uint)lParam) { - case WM_LBUTTONUP: - isInvoked = true; - mouseButton = MouseButton.Left; + case (uint)WndProcMessages.WM_LBUTTONUP: + InvokeIconInvoked(MouseButton.Left); break; - - case WM_MBUTTONUP: - isInvoked = true; - mouseButton = MouseButton.Middle; + case (uint)WndProcMessages.WM_MBUTTONUP: + InvokeIconInvoked(MouseButton.Middle); break; - - case WM_RBUTTONUP: - isInvoked = true; - mouseButton = MouseButton.Right; + case (uint)WndProcMessages.WM_RBUTTONUP: + InvokeIconInvoked(MouseButton.Right); break; } - - if (isInvoked) - { - IconInvoked?.Invoke(this, new NotificationIconInvokedEventArgs { MouseButton = mouseButton }); - } } return DefWindowProcW(hwnd, (uint)msg, wParam, (lParam)); diff --git a/NotificationFlyout.Wpf.UI/Helpers/RegistryHelper.cs b/NotificationFlyout.Wpf.UI/Helpers/RegistryHelper.cs deleted file mode 100644 index d5ad730..0000000 --- a/NotificationFlyout.Wpf.UI/Helpers/RegistryHelper.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.Win32; - -namespace NotificationFlyout.Wpf.UI.Helpers -{ - internal static class RegistryHelper - { - public static TValue GetDwordValue(string key, string valueName) - { - using var baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64); - using var subKey = baseKey.OpenSubKey(key); - - return (TValue)subKey.GetValue(valueName, 0); - } - } -} diff --git a/NotificationFlyout.Wpf.UI/Helpers/SystemInformationHelper.cs b/NotificationFlyout.Wpf.UI/Helpers/SystemInformationHelper.cs index e8adbde..cde40e5 100644 --- a/NotificationFlyout.Wpf.UI/Helpers/SystemInformationHelper.cs +++ b/NotificationFlyout.Wpf.UI/Helpers/SystemInformationHelper.cs @@ -8,13 +8,10 @@ namespace NotificationFlyout.Wpf.UI.Helpers public static class SystemInformationHelper { private const int SM_CXSCREEN = 0; - private const int SM_CYSCREEN = 1; - private const int SPI_GETWORKAREA = 48; public static Rect VirtualScreen => GetVirtualScreen(); - public static Rect WorkingArea => GetWorkingArea(); public static int GetCurrentDpi() diff --git a/NotificationFlyout.Wpf.UI/Helpers/SystemPersonalisationHelper.cs b/NotificationFlyout.Wpf.UI/Helpers/SystemPersonalisationHelper.cs new file mode 100644 index 0000000..fa7cdb3 --- /dev/null +++ b/NotificationFlyout.Wpf.UI/Helpers/SystemPersonalisationHelper.cs @@ -0,0 +1,69 @@ +using NotificationFlyout.Wpf.UI.Extensions; +using System; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interop; +using Windows.UI.ViewManagement; + +namespace NotificationFlyout.Wpf.UI.Helpers +{ + public class SystemPersonalisationHelper + { + private readonly UISettings _settings = new(); + + private SystemTheme _currentTheme; + + + private SystemPersonalisationHelper(Window window) + { + var source = HwndSource.FromHwnd(window.GetHandle()); + source.AddHook(new HwndSourceHook(WndProc)); + + _settings.ColorValuesChanged += OnColorValuesChanged; + _currentTheme = GetSystemTheme(); + } + + public event EventHandler ThemeChanged; + + public SystemTheme SystemTheme => GetSystemTheme(); + + public static SystemPersonalisationHelper Create(Window window) + { + return new SystemPersonalisationHelper(window); + } + + [DllImport("user32.dll", SetLastError = true)] + private static extern IntPtr DefWindowProcW(IntPtr handle, uint msg, IntPtr wParam, IntPtr lParam); + + private SystemTheme GetSystemTheme() + { + var uiTheme = _settings.GetColorValue(UIColorType.Background).ToString(); + return uiTheme == "#FFFFFFFF" ? SystemTheme.Light : SystemTheme.Dark; + } + + private void OnColorValuesChanged(UISettings sender, object args) + { + RaiseThemeChangedEvent(); + } + + private void RaiseThemeChangedEvent() + { + var theme = GetSystemTheme(); + if (theme != _currentTheme) + { + ThemeChanged?.Invoke(this, new ThemeChangedEventArgs(theme)); + _currentTheme = theme; + } + } + + private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + if (msg == (int)WndProcMessages.WM_SETTINGCHANGE) + { + RaiseThemeChangedEvent(); + } + + return DefWindowProcW(hwnd, (uint)msg, wParam, (lParam)); + } + } +} diff --git a/NotificationFlyout.Wpf.UI/Helpers/SystemSettingsHelper.cs b/NotificationFlyout.Wpf.UI/Helpers/SystemSettingsHelper.cs deleted file mode 100644 index b0d4d37..0000000 --- a/NotificationFlyout.Wpf.UI/Helpers/SystemSettingsHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using NotificationFlyout.Wpf.UI.Extensions; -using System; - -namespace NotificationFlyout.Wpf.UI.Helpers -{ - public static class SystemSettingsHelper - { - public static SystemTheme DefaultSystemTheme => GetDefaultSystemTheme(); - - private static SystemTheme GetDefaultSystemTheme() - { - return Environment.OSVersion.IsGreaterThan(OperatingSystemVersion.Windows10_1809) && DoesSystemUsesLightTheme() ? SystemTheme.Light : SystemTheme.Dark; - } - - private static bool DoesSystemUsesLightTheme() - { - var personalizeKey = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"; - return RegistryHelper.GetDwordValue(personalizeKey, "SystemUsesLightTheme") > 0; - } - } -} diff --git a/NotificationFlyout.Wpf.UI/Helpers/TaskbarHelper.cs b/NotificationFlyout.Wpf.UI/Helpers/TaskbarHelper.cs index 6808b48..859bceb 100644 --- a/NotificationFlyout.Wpf.UI/Helpers/TaskbarHelper.cs +++ b/NotificationFlyout.Wpf.UI/Helpers/TaskbarHelper.cs @@ -10,12 +10,9 @@ namespace NotificationFlyout.Wpf.UI.Helpers public class TaskbarHelper { private const string ShellTrayHandleName = "Shell_TrayWnd"; - private const int SPI_SETWORKAREA = 0x002F; - private const int WSETTINGCHANGE = 0x001A; - - private static readonly uint WTASKBARCREATED = PInvoke.RegisterWindowMessage("TaskbarCreated"); + private readonly uint WM_TASKBARCREATED = PInvoke.RegisterWindowMessage("TaskbarCreated"); private TaskbarHelper(Window window) { @@ -99,7 +96,7 @@ namespace NotificationFlyout.Wpf.UI.Helpers private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { - if (msg == WTASKBARCREATED || msg == WSETTINGCHANGE && (int)wParam == SPI_SETWORKAREA) + if (msg == WM_TASKBARCREATED || msg == (int)WndProcMessages.WM_SETTINGCHANGE && (int)wParam == SPI_SETWORKAREA) { TaskbarChanged?.Invoke(this, EventArgs.Empty); } diff --git a/NotificationFlyout.Wpf.UI/Helpers/ThemeChangedEventArgs.cs b/NotificationFlyout.Wpf.UI/Helpers/ThemeChangedEventArgs.cs new file mode 100644 index 0000000..6da9c84 --- /dev/null +++ b/NotificationFlyout.Wpf.UI/Helpers/ThemeChangedEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace NotificationFlyout.Wpf.UI.Helpers +{ + public class ThemeChangedEventArgs : EventArgs + { + internal ThemeChangedEventArgs(SystemTheme theme) + { + Theme = theme; + } + + public SystemTheme Theme { get; private set; } + } +} diff --git a/NotificationFlyout.Wpf.UI/Helpers/WndProcMessages.cs b/NotificationFlyout.Wpf.UI/Helpers/WndProcMessages.cs new file mode 100644 index 0000000..e6a2cb2 --- /dev/null +++ b/NotificationFlyout.Wpf.UI/Helpers/WndProcMessages.cs @@ -0,0 +1,11 @@ +namespace NotificationFlyout.Wpf.UI.Helpers +{ + internal enum WndProcMessages + { + WM_LBUTTONUP = 0x0202, + WM_MBUTTONUP = 0x0208, + WM_RBUTTONUP = 0x0205, + WM_MOUSEMOVE = 0x0200, + WM_SETTINGCHANGE = 0x001A, + } +} \ No newline at end of file diff --git a/NotificationFlyout.Wpf.UI/NotificationFlyout.Wpf.UI.csproj b/NotificationFlyout.Wpf.UI/NotificationFlyout.Wpf.UI.csproj index 0da0fef..4909a30 100644 --- a/NotificationFlyout.Wpf.UI/NotificationFlyout.Wpf.UI.csproj +++ b/NotificationFlyout.Wpf.UI/NotificationFlyout.Wpf.UI.csproj @@ -11,6 +11,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive +