From 4705bec8d6a6f033f7a8fe31a60866022f9a2364 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Wed, 19 Jul 2023 19:07:40 +0100 Subject: [PATCH] NotifyIcon context menu - initial prototype saving this here so i can see if there's a better way to do stuff first before adding functionality --- Bloxstrap/App.xaml.cs | 3 ++ Bloxstrap/UI/Elements/NotifyIconMenu.xaml | 35 +++++++++++++++ Bloxstrap/UI/Elements/NotifyIconMenu.xaml.cs | 46 ++++++++++++++++++++ Bloxstrap/UI/NotifyIconWrapper.cs | 27 +++++++++++- Bloxstrap/Utility/NativeMethods.cs | 12 +++++ 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 Bloxstrap/UI/Elements/NotifyIconMenu.xaml create mode 100644 Bloxstrap/UI/Elements/NotifyIconMenu.xaml.cs diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index 0125380..17b9d50 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -293,6 +293,9 @@ namespace Bloxstrap { Logger.WriteLine("[App::OnStartup] Bootstrapper task has finished"); + // notifyicon is blocking main thread, must be disposed here + NotifyIcon?.Dispose(); + if (t.IsFaulted) Logger.WriteLine("[App::OnStartup] An exception occurred when running the bootstrapper"); diff --git a/Bloxstrap/UI/Elements/NotifyIconMenu.xaml b/Bloxstrap/UI/Elements/NotifyIconMenu.xaml new file mode 100644 index 0000000..373a2b8 --- /dev/null +++ b/Bloxstrap/UI/Elements/NotifyIconMenu.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + diff --git a/Bloxstrap/UI/Elements/NotifyIconMenu.xaml.cs b/Bloxstrap/UI/Elements/NotifyIconMenu.xaml.cs new file mode 100644 index 0000000..d8e14d0 --- /dev/null +++ b/Bloxstrap/UI/Elements/NotifyIconMenu.xaml.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +using Bloxstrap.UI.ViewModels; + +namespace Bloxstrap.UI.Elements +{ + /// + /// Interaction logic for NotifyIconMenu.xaml + /// + public partial class NotifyIconMenu + { + public NotifyIconMenu() + { + InitializeComponent(); + } + + private void Window_Loaded(object? sender, RoutedEventArgs e) + { + // this is an awful hack lmao im so sorry to anyone who reads this + // https://stackoverflow.com/questions/357076/best-way-to-hide-a-window-from-the-alt-tab-program-switcher#:~:text=ShowInTaskbar%20%3D%20false%3B%20Owner%20%3D%20form1,form%27s%20ShowInTaskbar%20property%20to%20false. + var wndHelper = new WindowInteropHelper(this); + long exStyle = NativeMethods.GetWindowLongPtr(wndHelper.Handle, NativeMethods.GWL_EXSTYLE).ToInt64(); + exStyle |= NativeMethods.WS_EX_TOOLWINDOW; + NativeMethods.SetWindowLongPtr(wndHelper.Handle, NativeMethods.GWL_EXSTYLE, (IntPtr)exStyle); + } + + // i tried to use a viewmodel but uhhhhhhh it just didnt work idk + private void TestMenuItem_Click(object sender, RoutedEventArgs e) + { + Controls.ShowMessageBox($"hi how u doing i am {TestMenuItem.IsChecked}", MessageBoxImage.Warning); + } + } +} diff --git a/Bloxstrap/UI/NotifyIconWrapper.cs b/Bloxstrap/UI/NotifyIconWrapper.cs index ceda58e..14fcbdc 100644 --- a/Bloxstrap/UI/NotifyIconWrapper.cs +++ b/Bloxstrap/UI/NotifyIconWrapper.cs @@ -1,5 +1,7 @@ using System.Windows.Forms; +using Bloxstrap.UI.Elements; + namespace Bloxstrap.UI { public class NotifyIconWrapper : IDisposable @@ -7,9 +9,12 @@ namespace Bloxstrap.UI bool _disposed = false; private readonly NotifyIcon _notifyIcon; + private readonly NotifyIconMenu _contextMenuWrapper = new(); + EventHandler? _alertClickHandler; - public NotifyIconWrapper() + + public NotifyIconWrapper() { App.Logger.WriteLine("[NotifyIconWrapper::NotifyIconWrapper] Initializing notification area icon"); @@ -19,6 +24,21 @@ namespace Bloxstrap.UI Text = App.ProjectName, Visible = true }; + + _notifyIcon.MouseClick += MouseClickEventHandler; + + _contextMenuWrapper.Dispatcher.BeginInvoke(_contextMenuWrapper.ShowDialog); + + _contextMenuWrapper.Closing += (_, _) => App.Logger.WriteLine("[NotifyIconWrapper::NotifyIconWrapper] Context menu wrapper closing"); + } + + public void MouseClickEventHandler(object? sender, MouseEventArgs e) + { + if (e.Button != MouseButtons.Right) + return; + + _contextMenuWrapper.Activate(); + _contextMenuWrapper.ContextMenu.IsOpen = true; } public void ShowAlert(string caption, string message, int duration, EventHandler? clickHandler) @@ -62,8 +82,11 @@ namespace Bloxstrap.UI if (_disposed) return; + App.Logger.WriteLine($"[NotifyIconWrapper::Dispose] Disposing NotifyIcon"); + + _contextMenuWrapper.Dispatcher.Invoke(_contextMenuWrapper.Close); _notifyIcon.Dispose(); - + _disposed = true; GC.SuppressFinalize(this); diff --git a/Bloxstrap/Utility/NativeMethods.cs b/Bloxstrap/Utility/NativeMethods.cs index 321fbcf..b869f07 100644 --- a/Bloxstrap/Utility/NativeMethods.cs +++ b/Bloxstrap/Utility/NativeMethods.cs @@ -9,5 +9,17 @@ namespace Bloxstrap.Utility [DllImport("user32.dll")] public static extern bool FlashWindow(IntPtr hWnd, bool bInvert); + + [DllImport("user32.dll")] + public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong); + + // i only bothered to add the constants that im using lol + + public const int GWL_EXSTYLE = -20; + + public const int WS_EX_TOOLWINDOW = 0x00000080; } }