diff --git a/src/AutoCursorLock.App/App.xaml.cs b/src/AutoCursorLock.App/App.xaml.cs
index 4ef7d81..de5a0db 100644
--- a/src/AutoCursorLock.App/App.xaml.cs
+++ b/src/AutoCursorLock.App/App.xaml.cs
@@ -6,6 +6,7 @@ namespace AutoCursorLock.App;
using AutoCursorLock.App.Views;
using Microsoft.Extensions.DependencyInjection;
using System;
+using System.Linq;
using System.Windows;
///
@@ -27,6 +28,8 @@ public App()
///
public static ServiceProvider Services { get; private set; } = HostingExtensions.CreateContainer();
+ private MinimizeToTray? minimizeToTray;
+
///
/// Handles unhandled exceptions.
///
@@ -54,7 +57,24 @@ protected override async void OnStartup(StartupEventArgs e)
{
var mainWindowFactory = Services.GetRequiredService();
var mainWindow = await mainWindowFactory.CreateAsync();
- mainWindow.Show();
+
+ this.minimizeToTray = new MinimizeToTray(mainWindow);
+
+ // get arguments
+ var minimizeArg = e.Args.FirstOrDefault(arg => arg == "--minimize") is not null;
+ if (minimizeArg)
+ {
+ var quietArg = e.Args.FirstOrDefault(arg => arg == "--quiet") is not null;
+
+ mainWindow.WindowState = WindowState.Minimized;
+ this.minimizeToTray.UpdateTrayState(showBalloon: !quietArg);
+ }
+ else
+ {
+ mainWindow.Show();
+ }
+
+ this.minimizeToTray.StartWatching();
base.OnStartup(e);
}
diff --git a/src/AutoCursorLock.App/MinimizeToTray.cs b/src/AutoCursorLock.App/MinimizeToTray.cs
index 96c98cc..342b93b 100644
--- a/src/AutoCursorLock.App/MinimizeToTray.cs
+++ b/src/AutoCursorLock.App/MinimizeToTray.cs
@@ -13,86 +13,89 @@ namespace AutoCursorLock
///
/// Class implementing support for "minimize to tray" functionality.
///
- public static class MinimizeToTray
+ public class MinimizeToTray
{
+ private readonly Window window;
+ private NotifyIcon? notifyIcon;
+ private bool balloonShown;
+
///
- /// Enables "minimize to tray" behavior for the specified Window.
+ /// Initializes a new instance of the class.
///
- /// Window to enable the behavior for.
- public static void Enable(Window window)
+ /// Window instance to attach to.
+ public MinimizeToTray(Window window)
+ {
+ Debug.Assert(window != null, "window parameter is null.");
+ this.window = window;
+ }
+
+ public void StartWatching()
{
- // No need to track this instance; its event handlers will keep it alive
- new MinimizeToTrayInstance(window);
+ this.window.StateChanged += new EventHandler(HandleStateChanged);
}
///
- /// Class implementing "minimize to tray" functionality for a Window instance.
+ /// Handles the Window's StateChanged event.
///
- private class MinimizeToTrayInstance
+ /// Event source.
+ /// Event arguments.
+ private void HandleStateChanged(object? sender, EventArgs e)
{
- private Window window;
- private NotifyIcon? notifyIcon;
- private bool balloonShown;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Window instance to attach to.
- public MinimizeToTrayInstance(Window window)
- {
- Debug.Assert(window != null, "window parameter is null.");
- this.window = window;
- this.window.StateChanged += new EventHandler(HandleStateChanged);
- }
+ UpdateTrayState(showBalloon: true);
+ }
- ///
- /// Handles the Window's StateChanged event.
- ///
- /// Event source.
- /// Event arguments.
- private void HandleStateChanged(object? sender, EventArgs e)
+ public void UpdateTrayState(bool showBalloon = true)
+ {
+ if (this.notifyIcon == null)
{
- if (this.notifyIcon == null)
- {
- var icon = Assembly.GetEntryAssembly()?.Location;
-
- // Initialize NotifyIcon instance "on demand"
- this.notifyIcon = new NotifyIcon();
+ var icon = Assembly.GetEntryAssembly()?.Location;
- if (icon != null)
- {
- this.notifyIcon.Icon = Icon.ExtractAssociatedIcon(icon);
- }
+ // Initialize NotifyIcon instance "on demand"
+ this.notifyIcon = new NotifyIcon();
- this.notifyIcon.MouseClick += new MouseEventHandler(HandleNotifyIconOrBalloonClicked);
- this.notifyIcon.BalloonTipClicked += new EventHandler(HandleNotifyIconOrBalloonClicked);
+ if (icon != null)
+ {
+ this.notifyIcon.Icon = Icon.ExtractAssociatedIcon(icon);
}
- // Update copy of Window Title in case it has changed
- this.notifyIcon.Text = this.window.Title;
+ this.notifyIcon.MouseClick += new MouseEventHandler(HandleNotifyIconOrBalloonClicked);
+ this.notifyIcon.BalloonTipClicked += new EventHandler(HandleNotifyIconOrBalloonClicked);
+ }
- // Show/hide Window and NotifyIcon
- var minimized = this.window.WindowState == WindowState.Minimized;
- this.window.ShowInTaskbar = !minimized;
- this.notifyIcon.Visible = minimized;
- if (minimized && !this.balloonShown)
- {
- // If this is the first time minimizing to the tray, show the user what happened
- this.notifyIcon.ShowBalloonTip(1000, this.window.Title, "Minimized to tray...", ToolTipIcon.None);
- this.balloonShown = true;
- }
+ // Update copy of Window Title in case it has changed
+ this.notifyIcon.Text = this.window.Title;
+
+ // Show/hide Window and NotifyIcon
+ var minimized = this.window.WindowState == WindowState.Minimized;
+ this.window.ShowInTaskbar = !minimized;
+ this.notifyIcon.Visible = minimized;
+ if (showBalloon && minimized && !this.balloonShown)
+ {
+ // If this is the first time minimizing to the tray, show the user what happened
+ this.notifyIcon.ShowBalloonTip(1000, this.window.Title, "Minimized to tray...", ToolTipIcon.None);
+ this.balloonShown = true;
}
+ }
- ///
- /// Handles a click on the notify icon or its balloon.
- ///
- /// Event source.
- /// Event arguments.
- private void HandleNotifyIconOrBalloonClicked(object? sender, EventArgs e)
+ ///
+ /// Handles a click on the notify icon or its balloon.
+ ///
+ /// Event source.
+ /// Event arguments.
+ private void HandleNotifyIconOrBalloonClicked(object? sender, EventArgs e)
+ {
+ // Restore the Window
+ this.window.WindowState = WindowState.Normal;
+
+ // If the program was started with the --minimize argument, the Window has not been shown yet.
+ // Show the program and update the tray state one time since the StateChanged event does not fire on show.
+ if (!this.window.IsLoaded)
{
- // Restore the Window
- this.window.WindowState = WindowState.Normal;
+ this.window.Show();
+ UpdateTrayState();
}
+
+ this.window.Activate();
}
}
}
diff --git a/src/AutoCursorLock.App/Views/GeneralSettingsWindow.xaml b/src/AutoCursorLock.App/Views/GeneralSettingsWindow.xaml
new file mode 100644
index 0000000..32da081
--- /dev/null
+++ b/src/AutoCursorLock.App/Views/GeneralSettingsWindow.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Automatically start the application when Windows starts
+
+
+
+
+
diff --git a/src/AutoCursorLock.App/Views/GeneralSettingsWindow.xaml.cs b/src/AutoCursorLock.App/Views/GeneralSettingsWindow.xaml.cs
new file mode 100644
index 0000000..982d1a2
--- /dev/null
+++ b/src/AutoCursorLock.App/Views/GeneralSettingsWindow.xaml.cs
@@ -0,0 +1,70 @@
+// Copyright(c) James La Novara-Gsell. All Rights Reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+namespace AutoCursorLock.App.Views;
+
+using System;
+using System.Diagnostics;
+using System.Windows;
+
+///
+/// Interaction logic for GeneralSettings.xaml.
+///
+public partial class GeneralSettingsWindow : Window
+{
+ private readonly string shortcutPath;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GeneralSettingsWindow()
+ {
+ InitializeComponent();
+
+ // check if the shortcut exists in the startup folder
+ var startupFolder = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
+ this.shortcutPath = System.IO.Path.Combine(startupFolder, "AutoCursorLock.lnk");
+ StartWithWindows = System.IO.File.Exists(this.shortcutPath);
+
+ this.mainGrid.DataContext = this;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether to start the application when Windows starts.
+ ///
+ public bool StartWithWindows { get; set; }
+
+ private void StartWithWindowsCheckBox_Checked(object sender, RoutedEventArgs e)
+ {
+ if (System.IO.File.Exists(this.shortcutPath))
+ {
+ return;
+ }
+
+ // create a shortcut in the startup folder
+ var shortcutPath = this.shortcutPath;
+ var targetPath = Process.GetCurrentProcess().MainModule?.FileName ?? throw new AutoCursorLockException("Could not get entry assembly");
+
+ var shellType = Type.GetTypeFromProgID("WScript.Shell") ?? throw new AutoCursorLockException("Could not get WScript.Shell type");
+
+ // The use of dynamic here is unfortunate, but using the Activator removes a dependency on the IWshRuntimeLibrary
+ // and even if we used the full library, the IWshShell3.CreateShortctut method returns a dynamic...
+ dynamic shell = Activator.CreateInstance(shellType) ?? throw new AutoCursorLockException("Could not create WScript.Shell instance");
+
+ var shortcut = shell.CreateShortcut(shortcutPath);
+ shortcut.Description = "AutoCursorLock";
+ shortcut.TargetPath = targetPath;
+ shortcut.Arguments = "--minimize --quiet";
+ shortcut.Save();
+ }
+
+ private void StartWithWindowsCheckBox_Unchecked(object sender, RoutedEventArgs e)
+ {
+ // delete the shortcut in the startup folder
+ var shortcutPath = this.shortcutPath;
+ if (System.IO.File.Exists(shortcutPath))
+ {
+ System.IO.File.Delete(shortcutPath);
+ }
+ }
+}
diff --git a/src/AutoCursorLock.App/Views/MainWindow.xaml b/src/AutoCursorLock.App/Views/MainWindow.xaml
index 6cb1d17..0492254 100644
--- a/src/AutoCursorLock.App/Views/MainWindow.xaml
+++ b/src/AutoCursorLock.App/Views/MainWindow.xaml
@@ -8,7 +8,6 @@
xmlns:localExtensions="clr-namespace:AutoCursorLock.App.Extensions"
xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:models="clr-namespace:AutoCursorLock.App.Models"
xmlns:sdk="clr-namespace:AutoCursorLock.Sdk.Models;assembly=AutoCursorLock.Sdk"
- Loaded="Window_Loaded"
mc:Ignorable="d"
Title="AutoCursorLock"
Height="620"
@@ -39,6 +38,10 @@