From 5feb14871c05e38a4500209eccf9cddc60e78cf1 Mon Sep 17 00:00:00 2001 From: BobLd <38405645+BobLd@users.noreply.github.com> Date: Tue, 21 May 2024 06:07:36 +0100 Subject: [PATCH] Convert to block-scoped namespace --- Caly.Android/MainActivity.cs | 35 ++-- Caly.Core/App.axaml.cs | 283 +++++++++++++------------- Caly.Core/ViewModels/MainViewModel.cs | 123 +++++------ Caly.Core/ViewModels/ViewModelBase.cs | 33 +-- Caly.Core/Views/MainView.axaml.cs | 99 ++++----- Caly.Core/Views/MainWindow.axaml.cs | 31 +-- Caly.Desktop/Program.cs | 241 +++++++++++----------- 7 files changed, 426 insertions(+), 419 deletions(-) diff --git a/Caly.Android/MainActivity.cs b/Caly.Android/MainActivity.cs index 3c97fd0..f4abdf7 100644 --- a/Caly.Android/MainActivity.cs +++ b/Caly.Android/MainActivity.cs @@ -5,24 +5,25 @@ using Avalonia.Android; using Caly.Core; -namespace Caly.Android; - -[Activity( - Label = "Caly.Android", - Theme = "@style/MyTheme.NoActionBar", - Icon = "@drawable/icon", - MainLauncher = true, - ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)] -public class MainActivity : AvaloniaMainActivity +namespace Caly.Android { - protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) + [Activity( + Label = "Caly.Android", + Theme = "@style/MyTheme.NoActionBar", + Icon = "@drawable/icon", + MainLauncher = true, + ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)] + public class MainActivity : AvaloniaMainActivity { - return base.CustomizeAppBuilder(builder) - .WithInterFont() - .UseSkia() - .With(new AndroidPlatformOptions() - { - RenderingMode = new[] { AndroidRenderingMode.Software } - }); + protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) + { + return base.CustomizeAppBuilder(builder) + .WithInterFont() + .UseSkia() + .With(new AndroidPlatformOptions() + { + RenderingMode = new[] { AndroidRenderingMode.Software } + }); + } } } diff --git a/Caly.Core/App.axaml.cs b/Caly.Core/App.axaml.cs index 98e14e7..5a40ca4 100644 --- a/Caly.Core/App.axaml.cs +++ b/Caly.Core/App.axaml.cs @@ -30,196 +30,197 @@ using Caly.Core.Views; using Microsoft.Extensions.DependencyInjection; -namespace Caly.Core; - -public partial class App : Application +namespace Caly.Core { - private readonly FilePipeStream _pipeServer = new(); - private readonly CancellationTokenSource listeningToFilesCts = new(); - private Task? listeningToFiles; + public partial class App : Application + { + private readonly FilePipeStream _pipeServer = new(); + private readonly CancellationTokenSource listeningToFilesCts = new(); + private Task? listeningToFiles; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - private IPdfDocumentsService _pdfDocumentsService; + private IPdfDocumentsService _pdfDocumentsService; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public new static App? Current => Application.Current as App; + public new static App? Current => Application.Current as App; - /// - /// Gets the instance to resolve application services. - /// - public IServiceProvider? Services { get; private set; } + /// + /// Gets the instance to resolve application services. + /// + public IServiceProvider? Services { get; private set; } - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } - public override void OnFrameworkInitializationCompleted() - { - // Line below is needed to remove Avalonia data validation. - // Without this line you will get duplicate validations from both Avalonia and CT - BindingPlugins.DataValidators.RemoveAt(0); + public override void OnFrameworkInitializationCompleted() + { + // Line below is needed to remove Avalonia data validation. + // Without this line you will get duplicate validations from both Avalonia and CT + BindingPlugins.DataValidators.RemoveAt(0); - // Initialise dependencies - var services = new ServiceCollection(); + // Initialise dependencies + var services = new ServiceCollection(); - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new MainWindow + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - DataContext = new MainViewModel() - }; + desktop.MainWindow = new MainWindow + { + DataContext = new MainViewModel() + }; - services.AddSingleton(_ => (Visual)desktop.MainWindow); - desktop.Startup += Desktop_Startup; - desktop.Exit += Desktop_Exit; + services.AddSingleton(_ => (Visual)desktop.MainWindow); + desktop.Startup += Desktop_Startup; + desktop.Exit += Desktop_Exit; #if DEBUG - desktop.MainWindow.RendererDiagnostics.DebugOverlays = Avalonia.Rendering.RendererDebugOverlays.RenderTimeGraph; + desktop.MainWindow.RendererDiagnostics.DebugOverlays = Avalonia.Rendering.RendererDebugOverlays.RenderTimeGraph; #endif - } - else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) - { - singleViewPlatform.MainView = new MainView + } + else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) { - DataContext = new MainViewModel() - }; - services.AddSingleton(_ => (Visual)singleViewPlatform.MainView); - } + singleViewPlatform.MainView = new MainView + { + DataContext = new MainViewModel() + }; + services.AddSingleton(_ => (Visual)singleViewPlatform.MainView); + } - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddTransient(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(); - Services = services.BuildServiceProvider(); + Services = services.BuildServiceProvider(); - // We need to make sure IPdfDocumentsService singleton is initiated in UI thread + // We need to make sure IPdfDocumentsService singleton is initiated in UI thread #pragma warning disable CS8601 // Possible null reference assignment. - _pdfDocumentsService = Services.GetRequiredService(); + _pdfDocumentsService = Services.GetRequiredService(); #pragma warning restore CS8601 // Possible null reference assignment. - // TODO - Check https://github.com/AvaloniaUI/Avalonia/commit/0e014f9cb627d99fb4e1afa389b4c073c836e9b6 + // TODO - Check https://github.com/AvaloniaUI/Avalonia/commit/0e014f9cb627d99fb4e1afa389b4c073c836e9b6 - base.OnFrameworkInitializationCompleted(); - } - - private async void Desktop_Startup(object? sender, ControlledApplicationLifetimeStartupEventArgs e) - { - listeningToFiles = Task.Run(ListenToIncomingFiles); // Start listening - - if (e.Args.Length == 0) - { - return; + base.OnFrameworkInitializationCompleted(); } - await Task.Run(() => OpenDoc(e.Args[0], CancellationToken.None)); - } - - private void Desktop_Exit(object? sender, ControlledApplicationLifetimeExitEventArgs e) - { - listeningToFilesCts.Cancel(); - GC.KeepAlive(listeningToFiles); - - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + private async void Desktop_Startup(object? sender, ControlledApplicationLifetimeStartupEventArgs e) { - desktop.Startup -= Desktop_Startup; - desktop.Exit -= Desktop_Exit; - } - } + listeningToFiles = Task.Run(ListenToIncomingFiles); // Start listening - private async Task ListenToIncomingFiles() - { - Debug.ThrowOnUiThread(); + if (e.Args.Length == 0) + { + return; + } - try - { - await Parallel.ForEachAsync(_pipeServer.ReceivePathAsync(listeningToFilesCts.Token), - listeningToFilesCts.Token, - async (path, ct) => await OpenDoc(path, ct)); + await Task.Run(() => OpenDoc(e.Args[0], CancellationToken.None)); } - catch (OperationCanceledException) - { - // No op - } - catch (Exception ex) + + private void Desktop_Exit(object? sender, ControlledApplicationLifetimeExitEventArgs e) { - // Critical error... - ShowExceptionWindowSafely(ex); - Debug.WriteExceptionToFile(ex); - throw; + listeningToFilesCts.Cancel(); + GC.KeepAlive(listeningToFiles); + + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.Startup -= Desktop_Startup; + desktop.Exit -= Desktop_Exit; + } } - finally + + private async Task ListenToIncomingFiles() { - await _pipeServer.DisposeAsync(); - } - } + Debug.ThrowOnUiThread(); - private async Task OpenDoc(string? path, CancellationToken token) - { - Debug.ThrowOnUiThread(); + try + { + await Parallel.ForEachAsync(_pipeServer.ReceivePathAsync(listeningToFilesCts.Token), + listeningToFilesCts.Token, + async (path, ct) => await OpenDoc(path, ct)); + } + catch (OperationCanceledException) + { + // No op + } + catch (Exception ex) + { + // Critical error... + ShowExceptionWindowSafely(ex); + Debug.WriteExceptionToFile(ex); + throw; + } + finally + { + await _pipeServer.DisposeAsync(); + } + } - try + private async Task OpenDoc(string? path, CancellationToken token) { - if (string.IsNullOrEmpty(path) || !File.Exists(path)) + Debug.ThrowOnUiThread(); + + try { - var dialogService = Services?.GetRequiredService(); - if (dialogService is not null) + if (string.IsNullOrEmpty(path) || !File.Exists(path)) { - Dispatcher.UIThread.Post(() => dialogService.ShowNotification("Cannot open file", - "The file does not exist or the path is invalid.", NotificationType.Error)); - } + var dialogService = Services?.GetRequiredService(); + if (dialogService is not null) + { + Dispatcher.UIThread.Post(() => dialogService.ShowNotification("Cannot open file", + "The file does not exist or the path is invalid.", NotificationType.Error)); + } - // TODO - Log + // TODO - Log - return; - } + return; + } - await _pdfDocumentsService.OpenLoadDocument(path, token); - } - catch (Exception ex) - { - ShowExceptionNotificationSafely(ex); - Debug.WriteExceptionToFile(ex); + await _pdfDocumentsService.OpenLoadDocument(path, token); + } + catch (Exception ex) + { + ShowExceptionNotificationSafely(ex); + Debug.WriteExceptionToFile(ex); + } } - } - private void ShowExceptionNotificationSafely(Exception? ex) - { - Dispatcher.UIThread.Post(() => + private void ShowExceptionNotificationSafely(Exception? ex) { - try + Dispatcher.UIThread.Post(() => { - if (ex is null) return; + try + { + if (ex is null) return; - var dialogService = Services?.GetRequiredService(); - if (dialogService is not null) + var dialogService = Services?.GetRequiredService(); + if (dialogService is not null) + { + Dispatcher.UIThread.Post(() => dialogService.ShowNotification("Error", ex.Message, NotificationType.Error)); + } + } + catch { - Dispatcher.UIThread.Post(() => dialogService.ShowNotification("Error", ex.Message, NotificationType.Error)); + // No op } - } - catch - { - // No op - } - }); - } + }); + } - private void ShowExceptionWindowSafely(Exception? ex) - { - Dispatcher.UIThread.Post(() => + private void ShowExceptionWindowSafely(Exception? ex) { - try + Dispatcher.UIThread.Post(() => { - if (ex is null) return; + try + { + if (ex is null) return; - var dialogService = Services?.GetRequiredService(); - dialogService?.ShowExceptionWindow(ex); - } - catch - { - // No op - } - }); + var dialogService = Services?.GetRequiredService(); + dialogService?.ShowExceptionWindow(ex); + } + catch + { + // No op + } + }); + } } } diff --git a/Caly.Core/ViewModels/MainViewModel.cs b/Caly.Core/ViewModels/MainViewModel.cs index e91ce82..576c784 100644 --- a/Caly.Core/ViewModels/MainViewModel.cs +++ b/Caly.Core/ViewModels/MainViewModel.cs @@ -29,87 +29,88 @@ using Microsoft.Extensions.DependencyInjection; using Tabalonia.Controls; -namespace Caly.Core.ViewModels; - -public sealed partial class MainViewModel : ViewModelBase +namespace Caly.Core.ViewModels { - private readonly IDisposable _documentCollectionDisposable; + public sealed partial class MainViewModel : ViewModelBase + { + private readonly IDisposable _documentCollectionDisposable; - public ObservableCollection PdfDocuments { get; } = new(); + public ObservableCollection PdfDocuments { get; } = new(); - [ObservableProperty] private int _selectedDocumentIndex; + [ObservableProperty] private int _selectedDocumentIndex; - [ObservableProperty] private bool _isPaneOpen = !CalyExtensions.IsMobilePlatform(); + [ObservableProperty] private bool _isPaneOpen = !CalyExtensions.IsMobilePlatform(); - public MainViewModel() - { - // TODO - Dispose to unsubscribe - _documentCollectionDisposable = PdfDocuments - .GetWeakCollectionChangedObservable() - .ObserveOn(Scheduler.Default) - .Subscribe(async e => - { - try + public MainViewModel() + { + // TODO - Dispose to unsubscribe + _documentCollectionDisposable = PdfDocuments + .GetWeakCollectionChangedObservable() + .ObserveOn(Scheduler.Default) + .Subscribe(async e => { - if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems?.Count > 0) + try { - foreach (var newDoc in e.NewItems.OfType()) + if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems?.Count > 0) { - await Task.WhenAll(newDoc.LoadPagesTask, newDoc.LoadBookmarksTask); - } + foreach (var newDoc in e.NewItems.OfType()) + { + await Task.WhenAll(newDoc.LoadPagesTask, newDoc.LoadBookmarksTask); + } - SelectedDocumentIndex = PdfDocuments.Count - 1; + SelectedDocumentIndex = PdfDocuments.Count - 1; + } } - } - catch (OperationCanceledException) - { - // No op - } - catch (Exception ex) + catch (OperationCanceledException) + { + // No op + } + catch (Exception ex) + { + Debug.WriteExceptionToFile(ex); + Exception = new ExceptionViewModel(ex); + } + }); + } + + [RelayCommand] + private async Task OpenFile(CancellationToken token) + { + try + { + var pdfDocumentsService = App.Current?.Services?.GetRequiredService(); + if (pdfDocumentsService is null) { - Debug.WriteExceptionToFile(ex); - Exception = new ExceptionViewModel(ex); + throw new NullReferenceException($"Missing {nameof(IPdfDocumentsService)} instance."); } - }); - } - [RelayCommand] - private async Task OpenFile(CancellationToken token) - { - try - { - var pdfDocumentsService = App.Current?.Services?.GetRequiredService(); - if (pdfDocumentsService is null) + await pdfDocumentsService.OpenLoadDocument(token); + } + catch (OperationCanceledException) { - throw new NullReferenceException($"Missing {nameof(IPdfDocumentsService)} instance."); + // No op + } + catch (Exception e) + { + Debug.WriteExceptionToFile(e); + Exception = new ExceptionViewModel(e); } - - await pdfDocumentsService.OpenLoadDocument(token); - } - catch (OperationCanceledException) - { - // No op - } - catch (Exception e) - { - Debug.WriteExceptionToFile(e); - Exception = new ExceptionViewModel(e); } - } - [RelayCommand] - private async Task CloseTab(object tabItem) - { - // TODO - Finish proper dispose / unload of document on close - if (((DragTabItem)tabItem)?.DataContext is PdfDocumentViewModel vm) + [RelayCommand] + private async Task CloseTab(object tabItem) { - var pdfDocumentsService = App.Current?.Services?.GetRequiredService(); - if (pdfDocumentsService is null) + // TODO - Finish proper dispose / unload of document on close + if (((DragTabItem)tabItem)?.DataContext is PdfDocumentViewModel vm) { - throw new NullReferenceException($"Missing {nameof(IPdfDocumentsService)} instance."); - } + var pdfDocumentsService = App.Current?.Services?.GetRequiredService(); + if (pdfDocumentsService is null) + { + throw new NullReferenceException($"Missing {nameof(IPdfDocumentsService)} instance."); + } - await Task.Run(() => pdfDocumentsService.CloseUnloadDocument(vm)); + await Task.Run(() => pdfDocumentsService.CloseUnloadDocument(vm)); + } } } } \ No newline at end of file diff --git a/Caly.Core/ViewModels/ViewModelBase.cs b/Caly.Core/ViewModels/ViewModelBase.cs index 1976964..4b084b3 100644 --- a/Caly.Core/ViewModels/ViewModelBase.cs +++ b/Caly.Core/ViewModels/ViewModelBase.cs @@ -19,26 +19,27 @@ using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.Extensions.DependencyInjection; -namespace Caly.Core.ViewModels; - -public partial class ViewModelBase : ObservableObject +namespace Caly.Core.ViewModels { - [ObservableProperty] - private ExceptionViewModel? _exception; - - partial void OnExceptionChanging(ExceptionViewModel? value) + public partial class ViewModelBase : ObservableObject { - if (value is null) - { - return; - } + [ObservableProperty] + private ExceptionViewModel? _exception; - var dialogService = App.Current?.Services?.GetRequiredService(); - if (dialogService is null) + partial void OnExceptionChanging(ExceptionViewModel? value) { - throw new NullReferenceException($"Missing {nameof(IDialogService)} instance."); - } + if (value is null) + { + return; + } + + var dialogService = App.Current?.Services?.GetRequiredService(); + if (dialogService is null) + { + throw new NullReferenceException($"Missing {nameof(IDialogService)} instance."); + } - Dispatcher.UIThread.Post(() => dialogService.ShowExceptionWindowAsync(value)); + Dispatcher.UIThread.Post(() => dialogService.ShowExceptionWindowAsync(value)); + } } } diff --git a/Caly.Core/Views/MainView.axaml.cs b/Caly.Core/Views/MainView.axaml.cs index 30ed07d..e3a40c7 100644 --- a/Caly.Core/Views/MainView.axaml.cs +++ b/Caly.Core/Views/MainView.axaml.cs @@ -23,74 +23,75 @@ using Caly.Core.Services.Interfaces; using Microsoft.Extensions.DependencyInjection; -namespace Caly.Core.Views; - -public partial class MainView : UserControl +namespace Caly.Core.Views { - public MainView() + public partial class MainView : UserControl { - InitializeComponent(); - AddHandler(DragDrop.DropEvent, Drop); - } + public MainView() + { + InitializeComponent(); + AddHandler(DragDrop.DropEvent, Drop); + } - private async void Drop(object? sender, DragEventArgs e) - { - try + private async void Drop(object? sender, DragEventArgs e) { - if (e.Data is null || !e.Data.Contains(DataFormats.Files)) + try { - return; - } + if (e.Data is null || !e.Data.Contains(DataFormats.Files)) + { + return; + } - var files = e.Data.GetFiles(); + var files = e.Data.GetFiles(); - if (files is null) - { - return; - } + if (files is null) + { + return; + } - var pdfDocumentsService = App.Current?.Services?.GetRequiredService(); - if (pdfDocumentsService is null) + var pdfDocumentsService = App.Current?.Services?.GetRequiredService(); + if (pdfDocumentsService is null) + { + throw new NullReferenceException($"Missing {nameof(IPdfDocumentsService)} instance."); + } + + await Task.Run(() => pdfDocumentsService.OpenLoadDocuments(files, CancellationToken.None)); + } + catch (Exception ex) { - throw new NullReferenceException($"Missing {nameof(IPdfDocumentsService)} instance."); + // TODO - Show dialog + Debug.WriteExceptionToFile(ex); } - - await Task.Run(() => pdfDocumentsService.OpenLoadDocuments(files, CancellationToken.None)); - } - catch (Exception ex) - { - // TODO - Show dialog - Debug.WriteExceptionToFile(ex); } - } - private void TreeView_SizeChanged(object sender, SizeChangedEventArgs e) - { - if (!e.WidthChanged) + private void TreeView_SizeChanged(object sender, SizeChangedEventArgs e) { - return; - } - - if (sender is not TreeView treeView) - { - return; - } - - double width = treeView.Bounds.Width; + if (!e.WidthChanged) + { + return; + } - foreach (var control in treeView.GetRealizedContainers().OfType()) - { - var stackPanel = control.GetVisualChildren().OfType().FirstOrDefault(); - if (stackPanel is null) + if (sender is not TreeView treeView) { - continue; + return; } - stackPanel.SetCurrentValue(WidthProperty, width); + double width = treeView.Bounds.Width; - foreach (var textBlock in stackPanel.GetVisualDescendants().OfType()) + foreach (var control in treeView.GetRealizedContainers().OfType()) { - textBlock.InvalidateMeasure(); + var stackPanel = control.GetVisualChildren().OfType().FirstOrDefault(); + if (stackPanel is null) + { + continue; + } + + stackPanel.SetCurrentValue(WidthProperty, width); + + foreach (var textBlock in stackPanel.GetVisualDescendants().OfType()) + { + textBlock.InvalidateMeasure(); + } } } } diff --git a/Caly.Core/Views/MainWindow.axaml.cs b/Caly.Core/Views/MainWindow.axaml.cs index 4329de8..0d7eacb 100644 --- a/Caly.Core/Views/MainWindow.axaml.cs +++ b/Caly.Core/Views/MainWindow.axaml.cs @@ -17,29 +17,30 @@ using Avalonia.Controls.Notifications; using Avalonia.Interactivity; -namespace Caly.Core.Views; - -public partial class MainWindow : Window +namespace Caly.Core.Views { - public WindowNotificationManager NotificationManager { get; set; } - - public MainWindow() + public partial class MainWindow : Window { - InitializeComponent(); - } + public WindowNotificationManager NotificationManager { get; set; } - protected override void OnLoaded(RoutedEventArgs e) - { - NotificationManager = new WindowNotificationManager(this) + public MainWindow() + { + InitializeComponent(); + } + + protected override void OnLoaded(RoutedEventArgs e) { - Position = NotificationPosition.BottomRight, + NotificationManager = new WindowNotificationManager(this) + { + Position = NotificationPosition.BottomRight, #if DEBUG - MaxItems = 50 + MaxItems = 50 #else MaxItems = 5 #endif - }; + }; - base.OnLoaded(e); + base.OnLoaded(e); + } } } diff --git a/Caly.Desktop/Program.cs b/Caly.Desktop/Program.cs index e60f6d1..7cb0951 100644 --- a/Caly.Desktop/Program.cs +++ b/Caly.Desktop/Program.cs @@ -24,166 +24,167 @@ using Caly.Core.Utilities; using Microsoft.Extensions.DependencyInjection; -namespace Caly.Desktop; - -class Program +namespace Caly.Desktop { - private const string _appName = "Caly Pdf Reader"; - - private static readonly Mutex mutex = new Mutex(true, _appName); - - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - [STAThread] - public static void Main(string[] args) + class Program { - // Make sure the current directory is where the app is located, not where a file is opened - Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); + private const string _appName = "Caly Pdf Reader"; - bool isMainInstance = false; + private static readonly Mutex mutex = new Mutex(true, _appName); - try + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) { - // Make sure a single instance of the app is running - isMainInstance = mutex.WaitOne(TimeSpan.Zero, true); + // Make sure the current directory is where the app is located, not where a file is opened + Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); + + bool isMainInstance = false; - if (isMainInstance) + try { - SendToMainInstance(args); + // Make sure a single instance of the app is running + isMainInstance = mutex.WaitOne(TimeSpan.Zero, true); + + if (isMainInstance) + { + SendToMainInstance(args); + } + else // App instance already running + { + SendToRunningInstance(args); + } } - else // App instance already running + finally { - SendToRunningInstance(args); + if (isMainInstance) + { + mutex.ReleaseMutex(); + } } } - finally + + private static int SendToMainInstance(string[] args) { - if (isMainInstance) + try { - mutex.ReleaseMutex(); - } - } - } + // TODO - should the below be in App.axaml.cs? + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; - private static int SendToMainInstance(string[] args) - { - try - { - // TODO - should the below be in App.axaml.cs? - AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; + return BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + } + catch (Exception ex) + { + if (ex is AggregateException a) + { + ex = a.Flatten(); + } - return BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - } - catch (Exception ex) - { - if (ex is AggregateException a) + Debug.WriteExceptionToFile(ex); + throw; + } + finally { - ex = a.Flatten(); + AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException; + TaskScheduler.UnobservedTaskException -= TaskScheduler_UnobservedTaskException; } - - Debug.WriteExceptionToFile(ex); - throw; - } - finally - { - AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException; - TaskScheduler.UnobservedTaskException -= TaskScheduler_UnobservedTaskException; } - } - private static void SendToRunningInstance(string[] args) - { - try + private static void SendToRunningInstance(string[] args) { - if (args.Length == 0) + try { - // TODO - Still bring app to front even if there's no file to open - return; - } + if (args.Length == 0) + { + // TODO - Still bring app to front even if there's no file to open + return; + } - string path = args[0]; + string path = args[0]; - if (string.IsNullOrEmpty(path)) - { - return; + if (string.IsNullOrEmpty(path)) + { + return; + } + + FilePipeStream.SendPath(path); } + catch (Exception ex) + { + if (ex is AggregateException a) + { + ex = a.Flatten(); + } - FilePipeStream.SendPath(path); + Debug.WriteExceptionToFile(ex); + throw; + } } - catch (Exception ex) + + private static void ShowExceptionSafely(Exception? ex) { - if (ex is AggregateException a) + try { - ex = a.Flatten(); - } + if (ex is null) return; - Debug.WriteExceptionToFile(ex); - throw; + var dialogService = App.Current?.Services?.GetRequiredService(); + Dispatcher.UIThread.Post(() => dialogService?.ShowExceptionWindow(ex)); + } + catch + { + Debug.WriteExceptionToFile(ex); + } } - } - private static void ShowExceptionSafely(Exception? ex) - { - try + private static void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { - if (ex is null) return; + // https://docs.avaloniaui.net/docs/getting-started/unhandled-exceptions + // https://learn.microsoft.com/en-gb/dotnet/api/system.threading.tasks.taskscheduler.unobservedtaskexception?view=net-7.0 - var dialogService = App.Current?.Services?.GetRequiredService(); - Dispatcher.UIThread.Post(() => dialogService?.ShowExceptionWindow(ex)); - } - catch - { - Debug.WriteExceptionToFile(ex); + var exception = e.Exception.Flatten(); + ShowExceptionSafely(exception); + Debug.WriteExceptionToFile(exception); } - } - - private static void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) - { - // https://docs.avaloniaui.net/docs/getting-started/unhandled-exceptions - // https://learn.microsoft.com/en-gb/dotnet/api/system.threading.tasks.taskscheduler.unobservedtaskexception?view=net-7.0 - var exception = e.Exception.Flatten(); - ShowExceptionSafely(exception); - Debug.WriteExceptionToFile(exception); - } - - private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) - { - // https://github.com/AvaloniaUI/Avalonia/issues/5387 - if (e.ExceptionObject is not Exception exception) + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { - return; - } + // https://github.com/AvaloniaUI/Avalonia/issues/5387 + if (e.ExceptionObject is not Exception exception) + { + return; + } - if (exception is AggregateException aEx) - { - exception = aEx.Flatten(); + if (exception is AggregateException aEx) + { + exception = aEx.Flatten(); + } + ShowExceptionSafely(exception); + Debug.WriteExceptionToFile(exception); } - ShowExceptionSafely(exception); - Debug.WriteExceptionToFile(exception); - } - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() - { - try - { - return AppBuilder.Configure() - .UsePlatformDetect() - .WithInterFont() - .UseSkia() - // https://github.com/AvaloniaUI/Avalonia/discussions/12597 - .With(new Win32PlatformOptions { RenderingMode = new[] { Win32RenderingMode.Software } }) - .With(new X11PlatformOptions { RenderingMode = new[] { X11RenderingMode.Software }, WmClass = _appName }) - .With(new AvaloniaNativePlatformOptions { RenderingMode = new[] { AvaloniaNativeRenderingMode.Software } }) - .LogToTrace(); - } - catch (Exception e) + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() { - Debug.WriteExceptionToFile(e); - throw; + try + { + return AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .UseSkia() + // https://github.com/AvaloniaUI/Avalonia/discussions/12597 + .With(new Win32PlatformOptions { RenderingMode = new[] { Win32RenderingMode.Software } }) + .With(new X11PlatformOptions { RenderingMode = new[] { X11RenderingMode.Software }, WmClass = _appName }) + .With(new AvaloniaNativePlatformOptions { RenderingMode = new[] { AvaloniaNativeRenderingMode.Software } }) + .LogToTrace(); + } + catch (Exception e) + { + Debug.WriteExceptionToFile(e); + throw; + } } } }