Skip to content

Commit

Permalink
+ The app is now singleton, close #6
Browse files Browse the repository at this point in the history
  • Loading branch information
mhmd-azeez committed Dec 23, 2016
1 parent 7bb5752 commit 54446e6
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 112 deletions.
8 changes: 0 additions & 8 deletions RudeFox.FrontEnd/App.xaml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,12 @@
using RudeFox.Models;
using RudeFox.Helpers;

namespace RudeFox.FrontEnd
namespace RudeFox.ApplicationManagement
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
public class App : Application
{
#region Constructors
public App()
private App()
{
Operations = new ReadOnlyObservableCollection<OperationVM>(_operationsSource);
}
Expand All @@ -32,70 +29,29 @@ public App()
#region Properties
private static ObservableCollection<OperationVM> _operationsSource = new ObservableCollection<OperationVM>();
public static ReadOnlyObservableCollection<OperationVM> Operations { get; private set; }
#endregion

#region OnStartup
protected override async void OnStartup(StartupEventArgs e)
private static readonly Lazy<App> _instance = new Lazy<App>(() => new App());
public static App Instance
{
RegisterExceptionHandlingEvents();
InitializeComponents();

Task deleteTask = null;
if (e.Args.Length > 1 && e.Args[0].Equals(Constants.SENDTO_PREFIX, StringComparison.InvariantCultureIgnoreCase))
deleteTask = DeleteFilesOrFolders(e.Args.ToList());

// open the main window
var window = new MainWindow();
this.MainWindow = window;
window.Show();

if (deleteTask != null)
await deleteTask;
get { return _instance.Value; }
}
#endregion

#region Methods
private static void AddOperation(OperationVM operation)
#region OnStartup
protected override async void OnStartup(StartupEventArgs e)
{
_operationsSource.Add(operation);
}
this.MainWindow = new MainWindow();
this.MainWindow.Show();

public static void RemoveOperation(OperationVM operation)
{
_operationsSource.Remove(operation);
await ProcessCommandLineArgs(e.Args.ToList());
}

public static async Task DeleteFileOrFolder(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path should not be null or empty.");

var operation = new OperationVM { Path = path };
AddOperation(operation);

var task = ShredderService.Instance.ShredItemAsync(operation.Path, operation.CancellationTokenSource.Token, operation.TaskProgress);
try
{
await task;
}
catch (OperationCanceledException)
{
#endregion

}
catch (Exception exc)
{
LoggerService.Instance.Error(exc);
DialogService.Instance.GetErrorDialog("Could not delete item", exc).ShowDialog();
}
finally
{
await Current.Dispatcher.InvokeAsync(() => RemoveOperation(operation));
}
}
#region Methods

public static async Task DeleteFilesOrFolders(List<string> paths)
public async Task DeleteFilesOrFolders(List<string> paths)
{
paths.Remove(Constants.SENDTO_PREFIX);
var userAgreed = await GetUserAgreedToDeleteAsync(paths);
if (userAgreed != true) return;

Expand Down Expand Up @@ -124,8 +80,8 @@ public static async Task DeleteFilesOrFolders(List<string> paths)
var tempFolder = await UpdateManager.DownloadLatestUpdate(version);
if (tempFolder == null) return false; // no update

await Current.Dispatcher.BeginInvoke(new Action(() =>
Current.Exit += (sender, args) => UpdateManager.ApplyUpdate(tempFolder)
await Application.Current.Dispatcher.BeginInvoke(new Action(() =>
Application.Current.Exit += (sender, args) => UpdateManager.ApplyUpdate(tempFolder)
));
}
catch (Exception ex)
Expand All @@ -137,13 +93,68 @@ public static async Task DeleteFilesOrFolders(List<string> paths)
return true; // updated and waiting for the app to exit
}

private void LogUnhandledException(Exception e)
internal void Activate()
{
LoggerService.Instance.Error(e);
DialogService.Instance.GetErrorDialog("An unxpected error occured", e).ShowDialog();
MainWindow.Activate();
if (MainWindow.WindowState == WindowState.Minimized)
MainWindow.WindowState = WindowState.Normal;
}

private static async Task<bool?> GetUserAgreedToDeleteAsync(List<string> paths)
internal Task ProcessCommandLineArgs(List<string> args)
{
if (args == null) return null;

Task deleteTask = null;
if (args.Count > 1 && args[0].Equals(Constants.SENDTO_PREFIX, StringComparison.InvariantCultureIgnoreCase))
{
args.Remove(Constants.SENDTO_PREFIX);
deleteTask = DeleteFilesOrFolders(args);
}

return deleteTask;
}

internal void RegisterExceptionHandlingEvents()
{
// handle the unhandled global exceptions
AppDomain.CurrentDomain.UnhandledException += (sender, args) => LogUnhandledException((Exception)args.ExceptionObject);
App.Instance.DispatcherUnhandledException += (sender, args) => LogUnhandledException(args.Exception);
TaskScheduler.UnobservedTaskException += (s, args) => LogUnhandledException(args.Exception);
}

private async Task DeleteFileOrFolder(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path should not be null or empty.");

var operation = new OperationVM { Path = path };
AddOperation(operation);

var task = ShredderService.Instance.ShredItemAsync(operation.Path, operation.CancellationTokenSource.Token, operation.TaskProgress);
try
{
await task;
}
catch (OperationCanceledException)
{

}
catch (Exception exc)
{
LoggerService.Instance.Error(exc);
DialogService.Instance.GetErrorDialog("Could not delete item", exc).ShowDialog();
}
finally
{
await Application.Current.Dispatcher.InvokeAsync(() => RemoveOperation(operation));
}
}

private void AddOperation(OperationVM operation) => _operationsSource.Add(operation);

public void RemoveOperation(OperationVM operation) => _operationsSource.Remove(operation);

private async Task<bool?> GetUserAgreedToDeleteAsync(List<string> paths)
{
string message;
string okText = "Delete ";
Expand All @@ -160,28 +171,15 @@ private void LogUnhandledException(Exception e)
message = $"Are you sure you want to delete these {paths.Count} items?";
okText += "them";
}

return await DialogService.Instance.GetMessageDialog("Deleting items", message, MessageIcon.Exclamation, okText, "Cancel", true).ShowDialogAsync();
}

private void InitializeComponents()
{
// register sentry as NLog target
Target.Register<Nlog.SentryTarget>("Sentry");

#if !DEBUG
// check for updates
UpdateManager.Initialize(Keys.DROPBOX_API_KEY);
Task.Run(() => UpdateAfter(5));
#endif
var dialog = DialogService.Instance.GetMessageDialog("Deleting items", message, MessageIcon.Exclamation, okText, "Cancel", true);
dialog.Owner = this.MainWindow;
return await dialog.ShowDialogAsync();
}

private void RegisterExceptionHandlingEvents()
private void LogUnhandledException(Exception e)
{
// handle the unhandled global exceptions
AppDomain.CurrentDomain.UnhandledException += (sender, args) => LogUnhandledException((Exception)args.ExceptionObject);
DispatcherUnhandledException += (sender, args) => LogUnhandledException(args.Exception);
TaskScheduler.UnobservedTaskException += (s, args) => LogUnhandledException(args.Exception);
LoggerService.Instance.Error(e);
DialogService.Instance.GetErrorDialog("An unxpected error occured", e).ShowDialog();
}
#endregion
}
Expand Down
19 changes: 19 additions & 0 deletions RudeFox.FrontEnd/ApplicationManagement/EntryPoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;

namespace RudeFox.ApplicationManagement
{
public class EntryPoint
{
// Note:
// Rude Fox is a singleton application, meaning only one instance should be running at a time.
// The implementation is based on the Microsoft's WPF samples
// Link: https://github.com/Microsoft/WPF-Samples/tree/master/Application%20Management/SingleInstanceDetection

[STAThread]
public static void Main(string[] args)
{
var manager = new SingletonManager();
manager.Run(args);
}
}
}
53 changes: 53 additions & 0 deletions RudeFox.FrontEnd/ApplicationManagement/SingletonManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic.ApplicationServices;
using RudeFox.Helpers;
using RudeFox.Views;
using RudeFox.Services;
using NLog.Targets;

namespace RudeFox.ApplicationManagement
{
public class SingletonManager : WindowsFormsApplicationBase
{
public SingletonManager()
{
IsSingleInstance = true;
}

protected override bool OnStartup(StartupEventArgs e)
{
// first time app is launched
App.Instance.RegisterExceptionHandlingEvents();
InitializeComponents();
App.Instance.Run();
return false;
}

protected override async void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
// subsequent launches
Task deleteTask = App.Instance.ProcessCommandLineArgs(eventArgs.CommandLine?.ToList());

App.Instance.Activate();

if (deleteTask != null)
await deleteTask;
}

private void InitializeComponents()
{
// register sentry as NLog target
Target.Register<Nlog.SentryTarget>("Sentry");

#if !DEBUG
// check for updates
UpdateManager.Initialize(Keys.DROPBOX_API_KEY);
Task.Run(() => UpdateAfter(5));
#endif
}
}
}
12 changes: 4 additions & 8 deletions RudeFox.FrontEnd/RudeFox.FrontEnd.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<HintPath>..\packages\gong-wpf-dragdrop.1.0.0\lib\net45\GongSolutions.Wpf.DragDrop.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
Expand Down Expand Up @@ -75,14 +76,9 @@
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="ApplicationManagement\EntryPoint.cs" />
<Compile Include="ApplicationManagement\App.cs" />
<Compile Include="ApplicationManagement\SingletonManager.cs" />
<Compile Include="Controls\FlatButton.cs" />
<Compile Include="Controls\SidebarButton.cs" />
<Compile Include="Converters\TimespanToStringConverter.cs" />
Expand Down
16 changes: 5 additions & 11 deletions RudeFox.FrontEnd/ViewModels/MainWindowVM.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using RudeFox.Mvvm;
using System.Windows.Input;
using System.Windows;
using GongSolutions.Wpf.DragDrop;
using System.Collections.ObjectModel;
using System.IO;
using RudeFox.Models;
using RudeFox.Services;
using System.Threading;
using RudeFox.FrontEnd;
using Ookii.Dialogs.Wpf;
using System.Collections.Specialized;
using RudeFox.ApplicationManagement;

namespace RudeFox.ViewModels
{
Expand Down Expand Up @@ -73,7 +67,7 @@ async void IDropTarget.Drop(IDropInfo dropInfo)
if (data.GetDataPresent(DataFormats.FileDrop))
{
string[] paths = (string[])data.GetData(DataFormats.FileDrop);
await App.DeleteFilesOrFolders(paths.ToList());
await App.Instance.DeleteFilesOrFolders(paths.ToList());
}
}
#endregion
Expand All @@ -88,7 +82,7 @@ private async Task DeleteFiles()
if (result != true) return;

var files = dialog.FileNames;
await App.DeleteFilesOrFolders(files.ToList());
await App.Instance.DeleteFilesOrFolders(files.ToList());
}

private async Task DeleteFolders()
Expand All @@ -100,7 +94,7 @@ private async Task DeleteFolders()
if (result != true) return;

var path = dialog.SelectedPath;
await App.DeleteFilesOrFolders(new List<string> { path });
await App.Instance.DeleteFilesOrFolders(new List<string> { path });
}

#endregion
Expand Down
4 changes: 2 additions & 2 deletions RudeFox.FrontEnd/Views/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RudeFox.Views"
xmlns:frontEnd="clr-namespace:RudeFox.FrontEnd"
xmlns:appManagment="clr-namespace:RudeFox.ApplicationManagement"
mc:Ignorable="d"
xmlns:controls="clr-namespace:RudeFox.Controls"
xmlns:converters="clr-namespace:RudeFox.Converters"
Expand Down Expand Up @@ -126,7 +126,7 @@
</Grid>

<ListBox x:Name="lstOperations" gong:DragDrop.IsDropTarget="True" gong:DragDrop.DropHandler="{Binding}"
ItemsSource="{Binding Source={x:Static frontEnd:App.Operations}}"
ItemsSource="{Binding Source={x:Static appManagment:App.Operations}}"
HorizontalContentAlignment="Stretch" ItemTemplate="{StaticResource operation}"
Style="{StaticResource listBox}" Background="{StaticResource background2}" MouseDoubleClick="ListBox_MouseDoubleClick">
</ListBox>
Expand Down

0 comments on commit 54446e6

Please sign in to comment.