From 7513b4d83a01ec25cfc62fc806888a777f6aa1e3 Mon Sep 17 00:00:00 2001 From: Joe Du <13188169+joesdu@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:42:03 +0800 Subject: [PATCH 1/9] fix: Fix system tray right-click menu not displaying --- src/Wpf.Ui.Tray/Controls/NotifyIcon.cs | 36 +++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs index 32f1b2a6e..0b2091f2b 100644 --- a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs +++ b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs @@ -246,6 +246,9 @@ public NotifyIcon() internalNotifyIconManager = new Wpf.Ui.Tray.Internal.InternalNotifyIconManager(); RegisterHandlers(); + + // Listen for DataContext changes to update ContextMenu + DataContextChanged += OnDataContextChanged; } /// @@ -363,6 +366,9 @@ protected virtual void Dispose(bool disposing) System.Diagnostics.Debug.WriteLine($"INFO | {typeof(NotifyIcon)} disposed.", "Wpf.Ui.NotifyIcon"); + // Clean up event handlers + DataContextChanged -= OnDataContextChanged; + Unregister(); internalNotifyIconManager.Dispose(); @@ -375,6 +381,13 @@ protected virtual void Dispose(bool disposing) protected virtual void OnMenuChanged(ContextMenu contextMenu) { internalNotifyIconManager.ContextMenu = contextMenu; + + // Set the DataContext for ContextMenu to enable binding + if (contextMenu.DataContext == null && DataContext != null) + { + contextMenu.DataContext = DataContext; + } + internalNotifyIconManager.ContextMenu.SetCurrentValue(Control.FontSizeProperty, MenuFontSize); } @@ -410,11 +423,11 @@ private static void OnFocusOnLeftClickChanged(DependencyObject d, DependencyProp if (e.NewValue is not bool newValue) { notifyIcon.FocusOnLeftClick = false; - + notifyIcon.internalNotifyIconManager.FocusOnLeftClick = false; return; } - notifyIcon.FocusOnLeftClick = newValue; + notifyIcon.internalNotifyIconManager.FocusOnLeftClick = newValue; } private static void OnMenuOnRightClickChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) @@ -427,11 +440,11 @@ private static void OnMenuOnRightClickChanged(DependencyObject d, DependencyProp if (e.NewValue is not bool newValue) { notifyIcon.MenuOnRightClick = false; - + notifyIcon.internalNotifyIconManager.MenuOnRightClick = false; return; } - notifyIcon.MenuOnRightClick = newValue; + notifyIcon.internalNotifyIconManager.MenuOnRightClick = newValue; } private static void OnMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) @@ -455,6 +468,12 @@ private void InitializeIcon() internalNotifyIconManager.Icon = Icon; internalNotifyIconManager.MenuOnRightClick = MenuOnRightClick; internalNotifyIconManager.FocusOnLeftClick = FocusOnLeftClick; + + // Add Menu initialization + if (Menu != null) + { + OnMenuChanged(Menu); + } } private void RegisterHandlers() @@ -466,4 +485,13 @@ private void RegisterHandlers() internalNotifyIconManager.MiddleClick += OnMiddleClick; internalNotifyIconManager.MiddleDoubleClick += OnMiddleDoubleClick; } + + private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + // Update ContextMenu DataContext when NotifyIcon DataContext changes + if (Menu != null && e.NewValue != null) + { + Menu.DataContext = e.NewValue; + } + } } From f399109868373e46f8172be93b9c46b424f2d13b Mon Sep 17 00:00:00 2001 From: Joe Du <13188169+joesdu@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:42:36 +0800 Subject: [PATCH 2/9] fix: Fix system tray right-click menu not displaying --- src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs b/src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs index fdd299344..1f7d9267f 100644 --- a/src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs +++ b/src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs @@ -178,7 +178,10 @@ protected virtual void OpenMenu() // Without setting the handler window at the front, menu may appear behind the taskbar _ = Interop.User32.SetForegroundWindow(HookWindow.Handle); - ContextMenuService.SetPlacement(ContextMenu, PlacementMode.MousePoint); + + // Set placement properties for better positioning + ContextMenu.SetCurrentValue(ContextMenu.PlacementProperty, PlacementMode.MousePoint); + ContextMenu.SetCurrentValue(ContextMenu.PlacementTargetProperty, null); // ContextMenu.ApplyMica(); ContextMenu.SetCurrentValue(ContextMenu.IsOpenProperty, true); From 407812a608365855f832aed5f2582bce09cf758d Mon Sep 17 00:00:00 2001 From: Joe Du <13188169+joesdu@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:51:48 +0800 Subject: [PATCH 3/9] Update src/Wpf.Ui.Tray/Controls/NotifyIcon.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Wpf.Ui.Tray/Controls/NotifyIcon.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs index 0b2091f2b..f74b94dfd 100644 --- a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs +++ b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs @@ -427,7 +427,7 @@ private static void OnFocusOnLeftClickChanged(DependencyObject d, DependencyProp return; } - notifyIcon.internalNotifyIconManager.FocusOnLeftClick = newValue; + notifyIcon.FocusOnLeftClick = newValue; } private static void OnMenuOnRightClickChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) From c508dccc7a36d101e66b0ef55b0c01e4e449dc09 Mon Sep 17 00:00:00 2001 From: Joe Du <13188169+joesdu@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:51:57 +0800 Subject: [PATCH 4/9] Update src/Wpf.Ui.Tray/Controls/NotifyIcon.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Wpf.Ui.Tray/Controls/NotifyIcon.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs index f74b94dfd..0b773ae82 100644 --- a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs +++ b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs @@ -440,7 +440,6 @@ private static void OnMenuOnRightClickChanged(DependencyObject d, DependencyProp if (e.NewValue is not bool newValue) { notifyIcon.MenuOnRightClick = false; - notifyIcon.internalNotifyIconManager.MenuOnRightClick = false; return; } From 2227c4784549e36d0730e2c006d776f7cf1a1fcc Mon Sep 17 00:00:00 2001 From: Joe Du <13188169+joesdu@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:52:03 +0800 Subject: [PATCH 5/9] Update src/Wpf.Ui.Tray/Controls/NotifyIcon.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Wpf.Ui.Tray/Controls/NotifyIcon.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs index 0b773ae82..940ed5fd6 100644 --- a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs +++ b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs @@ -443,7 +443,7 @@ private static void OnMenuOnRightClickChanged(DependencyObject d, DependencyProp return; } - notifyIcon.internalNotifyIconManager.MenuOnRightClick = newValue; + notifyIcon.MenuOnRightClick = newValue; } private static void OnMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) From 49dfa3b1e8a1be312801e855ead743e6fa2f0916 Mon Sep 17 00:00:00 2001 From: Joe Du <13188169+joesdu@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:52:09 +0800 Subject: [PATCH 6/9] Update src/Wpf.Ui.Tray/Controls/NotifyIcon.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Wpf.Ui.Tray/Controls/NotifyIcon.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs index 940ed5fd6..729fe56f7 100644 --- a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs +++ b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs @@ -423,7 +423,6 @@ private static void OnFocusOnLeftClickChanged(DependencyObject d, DependencyProp if (e.NewValue is not bool newValue) { notifyIcon.FocusOnLeftClick = false; - notifyIcon.internalNotifyIconManager.FocusOnLeftClick = false; return; } From 366209b78d964054f8fe9ffd939d15c5cbd958f9 Mon Sep 17 00:00:00 2001 From: Joes Date: Mon, 28 Jul 2025 12:01:14 +0800 Subject: [PATCH 7/9] feat: add tray menu demo --- .../ViewModels/Windows/MainWindowViewModel.cs | 25 ++++- .../Views/Windows/MainWindow.xaml.cs | 93 +++++++++++++++++++ 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs index 8132dd717..9edaa4c2f 100644 --- a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs +++ b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs @@ -63,7 +63,7 @@ public partial class MainWindowViewModel(IStringLocalizer localize new NavigationViewItem(nameof(ThumbRate), typeof(ThumbRatePage)), new NavigationViewItem(nameof(SplitButton), typeof(SplitButtonPage)), new NavigationViewItem(nameof(Slider), typeof(SliderPage)), - }, + } }, new NavigationViewItem { @@ -182,9 +182,26 @@ public partial class MainWindowViewModel(IStringLocalizer localize ]; [ObservableProperty] - private ObservableCollection _trayMenuItems = + private ObservableCollection _trayMenuItems = [ - new Wpf.Ui.Controls.MenuItem { Header = "Home", Tag = "tray_home" }, - new Wpf.Ui.Controls.MenuItem { Header = "Close", Tag = "tray_close" }, + new Wpf.Ui.Controls.MenuItem() + { + Header = "Home", + Tag = "tray_home", + Icon = new SymbolIcon { Symbol = SymbolRegular.Home24 } + }, + new Wpf.Ui.Controls.MenuItem() + { + Header = "Settings", + Tag = "tray_settings", + Icon = new SymbolIcon { Symbol = SymbolRegular.Settings24 } + }, + new Separator(), + new Wpf.Ui.Controls.MenuItem() + { + Header = "Close", + Tag = "tray_close", + Icon = new SymbolIcon { Symbol = SymbolRegular.Dismiss24 } + }, ]; } diff --git a/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml.cs b/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml.cs index cf2543f77..5dd0bdf6a 100644 --- a/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml.cs +++ b/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml.cs @@ -30,6 +30,7 @@ IContentDialogService contentDialogService snackbarService.SetSnackbarPresenter(SnackbarPresenter); navigationService.SetNavigationControl(NavigationView); contentDialogService.SetDialogHost(RootContentDialog); + SetupTrayMenuEvents(); } public MainWindowViewModel ViewModel { get; } @@ -38,6 +39,98 @@ IContentDialogService contentDialogService private bool _isPaneOpenedOrClosedFromCode; + private void SetupTrayMenuEvents() + { + foreach (var menuItem in ViewModel.TrayMenuItems) + { + if (menuItem is MenuItem item) + { + item.Click += OnTrayMenuItemClick; + } + } + } + + private void OnTrayMenuItemClick(object sender, RoutedEventArgs e) + { + if (sender is not Wpf.Ui.Controls.MenuItem menuItem) + { + return; + } + + var tag = menuItem.Tag?.ToString() ?? string.Empty; + + Debug.WriteLine($"System Tray Click: {menuItem.Header}, Tag: {tag}"); + + switch (tag) + { + case "tray_home": + HandleTrayHomeClick(); + break; + case "tray_settings": + HandleTraySettingsClick(); + break; + case "tray_close": + HandleTrayCloseClick(); + break; + default: + if (!string.IsNullOrEmpty(tag)) + { + System.Diagnostics.Debug.WriteLine($"unknown Tag: {tag}"); + } + + break; + } + } + + private void HandleTrayHomeClick() + { + System.Diagnostics.Debug.WriteLine("Tray menu - Home Click"); + + ShowAndActivateWindow(); + + NavigateToPage(typeof(DashboardPage)); + } + + private void HandleTraySettingsClick() + { + System.Diagnostics.Debug.WriteLine("Tray menu - Settings Click"); + + ShowAndActivateWindow(); + + NavigateToPage(typeof(SettingsPage)); + } + + private static void HandleTrayCloseClick() + { + System.Diagnostics.Debug.WriteLine("Tray menu - Close Click"); + + Application.Current.Shutdown(); + } + + private void ShowAndActivateWindow() + { + if (WindowState == WindowState.Minimized) + { + SetCurrentValue(WindowStateProperty, WindowState.Normal); + } + + Show(); + _ = Activate(); + _ = Focus(); + } + + private void NavigateToPage(Type pageType) + { + try + { + NavigationView.Navigate(pageType); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"NavigateToPage {pageType.Name} Error: {ex.Message}"); + } + } + private void OnNavigationSelectionChanged(object sender, RoutedEventArgs e) { if (sender is not Wpf.Ui.Controls.NavigationView navigationView) From 460646aba4e986ca4856f15e7e8bb499ed4cc304 Mon Sep 17 00:00:00 2001 From: Joe Du <13188169+joesdu@users.noreply.github.com> Date: Mon, 25 Aug 2025 09:12:53 +0800 Subject: [PATCH 8/9] Comment out Menu DataContext update in NotifyIcon Commented out the line that updates Menu's DataContext to prevent potential issues. --- src/Wpf.Ui.Tray/Controls/NotifyIcon.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs index 729fe56f7..57197c7c2 100644 --- a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs +++ b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs @@ -486,8 +486,8 @@ private void RegisterHandlers() private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { - // Update ContextMenu DataContext when NotifyIcon DataContext changes - if (Menu != null && e.NewValue != null) + // Menu?.DataContext = e.NewValue; + if (Menu != null) { Menu.DataContext = e.NewValue; } From 40af72020600cc1c69f894cad5e8cbc34e8cdf66 Mon Sep 17 00:00:00 2001 From: Joe Du <13188169+joesdu@users.noreply.github.com> Date: Mon, 25 Aug 2025 09:14:36 +0800 Subject: [PATCH 9/9] Change trayMenuItems type from object to Control --- src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs index 9edaa4c2f..037a63721 100644 --- a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs +++ b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs @@ -182,7 +182,7 @@ public partial class MainWindowViewModel(IStringLocalizer localize ]; [ObservableProperty] - private ObservableCollection _trayMenuItems = + private ObservableCollection _trayMenuItems = [ new Wpf.Ui.Controls.MenuItem() {