Enhanced startup- and shutdown-procedure by introducing a stack of IOperations (which are automatically reverted on shutdown or if an error happens during startup).

This commit is contained in:
Damian Büchel 2017-07-21 10:04:27 +02:00
parent eb6fbf49b8
commit 1153fea091
18 changed files with 429 additions and 167 deletions

View file

@ -18,7 +18,7 @@ namespace SafeExamBrowser.Configuration
public class WorkingArea : IWorkingArea public class WorkingArea : IWorkingArea
{ {
private ILogger logger; private ILogger logger;
private RECT? initial; private RECT? originalWorkingArea;
public WorkingArea(ILogger logger) public WorkingArea(ILogger logger)
{ {
@ -27,9 +27,9 @@ namespace SafeExamBrowser.Configuration
public void InitializeFor(ITaskbar taskbar) public void InitializeFor(ITaskbar taskbar)
{ {
initial = User32.GetWorkingArea(); originalWorkingArea = User32.GetWorkingArea();
LogWorkingArea("Saved initial working area", initial.Value); LogWorkingArea("Saved original working area", originalWorkingArea.Value);
var area = new RECT var area = new RECT
{ {
@ -39,17 +39,17 @@ namespace SafeExamBrowser.Configuration
Bottom = Screen.PrimaryScreen.Bounds.Height - taskbar.GetAbsoluteHeight() Bottom = Screen.PrimaryScreen.Bounds.Height - taskbar.GetAbsoluteHeight()
}; };
LogWorkingArea("Setting new working area", area); LogWorkingArea("Trying to set new working area", area);
User32.SetWorkingArea(area); User32.SetWorkingArea(area);
LogWorkingArea("Working area is now set to", User32.GetWorkingArea()); LogWorkingArea("Working area is now set to", User32.GetWorkingArea());
} }
public void Reset() public void Reset()
{ {
if (initial.HasValue) if (originalWorkingArea.HasValue)
{ {
User32.SetWorkingArea(initial.Value); User32.SetWorkingArea(originalWorkingArea.Value);
LogWorkingArea("Restored initial working area", initial.Value); LogWorkingArea("Restored original working area", originalWorkingArea.Value);
} }
} }

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Contracts.Behaviour
{
public interface IOperation
{
/// <summary>
/// The splash screen to be used to show status information to the user.
/// </summary>
ISplashScreen SplashScreen { set; }
/// <summary>
/// Performs the operation.
/// </summary>
void Perform();
/// <summary>
/// Reverts all changes which were made when performing the operation.
/// </summary>
void Revert();
}
}

View file

@ -6,6 +6,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System.Collections.Generic;
namespace SafeExamBrowser.Contracts.Behaviour namespace SafeExamBrowser.Contracts.Behaviour
{ {
public interface IShutdownController public interface IShutdownController
@ -13,6 +15,6 @@ namespace SafeExamBrowser.Contracts.Behaviour
/// <summary> /// <summary>
/// Reverts any changes performed during the startup or runtime and releases all used resources. /// Reverts any changes performed during the startup or runtime and releases all used resources.
/// </summary> /// </summary>
void FinalizeApplication(); void FinalizeApplication(Stack<IOperation> operations);
} }
} }

View file

@ -6,14 +6,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System.Collections.Generic;
namespace SafeExamBrowser.Contracts.Behaviour namespace SafeExamBrowser.Contracts.Behaviour
{ {
public interface IStartupController public interface IStartupController
{ {
/// <summary> /// <summary>
/// Tries to initialize the application. Returns <c>true</c> if the initialization was successful, /// Tries to initialize the application. Returns <c>true</c> if the initialization was successful,
/// <c>false</c> otherwise. /// <c>false</c> otherwise. All operations performed during the startup procedure will be registered
/// to the given <c>out</c> parameter.
/// </summary> /// </summary>
bool TryInitializeApplication(); bool TryInitializeApplication(out Stack<IOperation> operations);
} }
} }

View file

@ -42,6 +42,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Behaviour\IApplicationController.cs" /> <Compile Include="Behaviour\IApplicationController.cs" />
<Compile Include="Behaviour\INotificationController.cs" /> <Compile Include="Behaviour\INotificationController.cs" />
<Compile Include="Behaviour\IOperation.cs" />
<Compile Include="Configuration\IIconResource.cs" /> <Compile Include="Configuration\IIconResource.cs" />
<Compile Include="Configuration\IApplicationInfo.cs" /> <Compile Include="Configuration\IApplicationInfo.cs" />
<Compile Include="Configuration\IApplicationInstance.cs" /> <Compile Include="Configuration\IApplicationInstance.cs" />

View file

@ -22,16 +22,27 @@ namespace SafeExamBrowser.Contracts.UserInterface
/// </summary> /// </summary>
void InvokeShow(); void InvokeShow();
/// <summary>
/// Updates the progress bar of the splash screen according to the specified amount.
/// </summary>
void Progress(int amount = 1);
/// <summary>
/// Regresses the progress bar of the splash screen according to the specified amount.
/// </summary>
void Regress(int amount = 1);
/// <summary>
/// Sets the style of the progress bar to indeterminate, i.e. <c>Progress</c> and
/// <c>Regress</c> won't have any effect when called.
/// </summary>
void SetIndeterminate();
/// <summary> /// <summary>
/// Set the maximum of the splash screen's progress bar. /// Set the maximum of the splash screen's progress bar.
/// </summary> /// </summary>
void SetMaxProgress(int max); void SetMaxProgress(int max);
/// <summary>
/// Updates the progress bar of the splash screen according to the specified amount.
/// </summary>
void UpdateProgress(int amount = 1);
/// <summary> /// <summary>
/// Updates the status text of the splash screen. If the busy flag is set, /// Updates the status text of the splash screen. If the busy flag is set,
/// the splash screen will show an animation to indicate a long-running operation. /// the splash screen will show an animation to indicate a long-running operation.

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations
{
class BrowserInitializationOperation : IOperation
{
private IApplicationController browserController;
private IApplicationInfo browserInfo;
private ILogger logger;
private ITaskbar taskbar;
private IUiElementFactory uiFactory;
public ISplashScreen SplashScreen { private get; set; }
public BrowserInitializationOperation(
IApplicationController browserController,
IApplicationInfo browserInfo,
ILogger logger,
ITaskbar taskbar,
IUiElementFactory uiFactory)
{
this.browserController = browserController;
this.browserInfo = browserInfo;
this.logger = logger;
this.taskbar = taskbar;
this.uiFactory = uiFactory;
}
public void Perform()
{
logger.Info("--- Initializing browser ---");
SplashScreen.UpdateText(Key.SplashScreen_InitializeBrowser);
var browserButton = uiFactory.CreateApplicationButton(browserInfo);
browserController.RegisterApplicationButton(browserButton);
taskbar.AddButton(browserButton);
}
public void Revert()
{
// Nothing to do here so far...
}
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations
{
class ProcessMonitoringOperation : IOperation
{
private ILogger logger;
private IProcessMonitor processMonitor;
public ISplashScreen SplashScreen { private get; set; }
public ProcessMonitoringOperation(ILogger logger, IProcessMonitor processMonitor)
{
this.logger = logger;
this.processMonitor = processMonitor;
}
public void Perform()
{
logger.Info("--- Initializing process monitoring ---");
SplashScreen.UpdateText(Key.SplashScreen_InitializeProcessMonitoring);
// TODO
}
public void Revert()
{
logger.Info("--- Stopping process monitoring ---");
SplashScreen.UpdateText(Key.SplashScreen_StopProcessMonitoring);
// TODO
}
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations
{
class TaskbarInitializationOperation : IOperation
{
private ILogger logger;
private ITaskbar taskbar;
private IUiElementFactory uiFactory;
private INotificationInfo aboutInfo;
public ISplashScreen SplashScreen { private get; set; }
public TaskbarInitializationOperation(ILogger logger, INotificationInfo aboutInfo, ITaskbar taskbar, IUiElementFactory uiFactory)
{
this.logger = logger;
this.aboutInfo = aboutInfo;
this.taskbar = taskbar;
this.uiFactory = uiFactory;
}
public void Perform()
{
logger.Info("--- Initializing taskbar ---");
SplashScreen.UpdateText(Key.SplashScreen_InitializeTaskbar);
var aboutNotification = uiFactory.CreateNotification(aboutInfo);
taskbar.AddNotification(aboutNotification);
}
public void Revert()
{
// Nothing to do here so far...
}
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations
{
class WorkingAreaOperation : IOperation
{
private ILogger logger;
private IProcessMonitor processMonitor;
private ITaskbar taskbar;
private IWorkingArea workingArea;
public ISplashScreen SplashScreen { private get; set; }
public WorkingAreaOperation(ILogger logger, IProcessMonitor processMonitor, ITaskbar taskbar, IWorkingArea workingArea)
{
this.logger = logger;
this.processMonitor = processMonitor;
this.taskbar = taskbar;
this.workingArea = workingArea;
}
public void Perform()
{
logger.Info("--- Initializing working area ---");
SplashScreen.UpdateText(Key.SplashScreen_WaitExplorerTermination, true);
processMonitor.CloseExplorerShell();
processMonitor.StartMonitoringExplorer();
// TODO
// - Minimizing all open windows
// - Emptying clipboard
SplashScreen.UpdateText(Key.SplashScreen_InitializeWorkingArea);
workingArea.InitializeFor(taskbar);
}
public void Revert()
{
logger.Info("--- Restoring working area ---");
SplashScreen.UpdateText(Key.SplashScreen_RestoreWorkingArea);
// TODO
// - Restore all windows?
// - Emptying clipboard
workingArea.Reset();
SplashScreen.UpdateText(Key.SplashScreen_WaitExplorerStartup, true);
processMonitor.StopMonitoringExplorer();
processMonitor.StartExplorerShell();
}
}
}

View file

@ -30,16 +30,6 @@ namespace SafeExamBrowser.Core.Behaviour
private IUiElementFactory uiFactory; private IUiElementFactory uiFactory;
private IWorkingArea workingArea; private IWorkingArea workingArea;
private IEnumerable<Action> ShutdownOperations
{
get
{
yield return StopProcessMonitoring;
yield return RestoreWorkingArea;
yield return FinalizeApplicationLog;
}
}
public ShutdownController( public ShutdownController(
ILogger logger, ILogger logger,
IMessageBox messageBox, IMessageBox messageBox,
@ -58,64 +48,62 @@ namespace SafeExamBrowser.Core.Behaviour
this.workingArea = workingArea; this.workingArea = workingArea;
} }
public void FinalizeApplication() public void FinalizeApplication(Stack<IOperation> operations)
{ {
try try
{ {
InitializeSplashScreen(); InitializeSplashScreen();
RevertOperations(operations);
foreach (var operation in ShutdownOperations) FinalizeApplicationLog();
{
operation();
splashScreen.UpdateProgress();
// TODO: Remove!
Thread.Sleep(250);
}
} }
catch (Exception e) catch (Exception e)
{ {
logger.Error($"Failed to finalize application!", e); LogAndShowException(e);
messageBox.Show(text.Get(Key.MessageBox_ShutdownError), text.Get(Key.MessageBox_ShutdownErrorTitle), icon: MessageBoxIcon.Error); FinalizeApplicationLog(false);
}
}
private void RevertOperations(Stack<IOperation> operations)
{
while (operations.Any())
{
var operation = operations.Pop();
operation.SplashScreen = splashScreen;
operation.Revert();
splashScreen.Progress();
// TODO: Remove!
Thread.Sleep(250);
} }
} }
private void InitializeSplashScreen() private void InitializeSplashScreen()
{ {
splashScreen = uiFactory.CreateSplashScreen(settings, text); splashScreen = uiFactory.CreateSplashScreen(settings, text);
splashScreen.SetMaxProgress(ShutdownOperations.Count()); splashScreen.SetIndeterminate();
splashScreen.UpdateText(Key.SplashScreen_ShutdownProcedure); splashScreen.UpdateText(Key.SplashScreen_ShutdownProcedure);
splashScreen.InvokeShow(); splashScreen.InvokeShow();
logger.Info("--- Initiating shutdown procedure ---"); logger.Info("--- Initiating shutdown procedure ---");
} }
private void StopProcessMonitoring() private void LogAndShowException(Exception e)
{ {
logger.Info("--- Stopping process monitoring ---"); logger.Error($"Failed to finalize application!", e);
splashScreen.UpdateText(Key.SplashScreen_StopProcessMonitoring); messageBox.Show(text.Get(Key.MessageBox_ShutdownError), text.Get(Key.MessageBox_ShutdownErrorTitle), icon: MessageBoxIcon.Error);
// TODO
processMonitor.StopMonitoringExplorer();
} }
private void RestoreWorkingArea() private void FinalizeApplicationLog(bool success = true)
{ {
logger.Info("--- Restoring working area ---"); if (success)
splashScreen.UpdateText(Key.SplashScreen_RestoreWorkingArea);
// TODO
workingArea.Reset();
splashScreen.UpdateText(Key.SplashScreen_WaitExplorerStartup, true);
processMonitor.StartExplorerShell();
}
private void FinalizeApplicationLog()
{ {
logger.Info("--- Application successfully finalized! ---"); logger.Info("--- Application successfully finalized! ---");
}
else
{
logger.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); logger.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
} }
} }
}
} }

View file

@ -16,6 +16,7 @@ using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Behaviour.Operations;
namespace SafeExamBrowser.Core.Behaviour namespace SafeExamBrowser.Core.Behaviour
{ {
@ -34,21 +35,7 @@ namespace SafeExamBrowser.Core.Behaviour
private IUiElementFactory uiFactory; private IUiElementFactory uiFactory;
private IWorkingArea workingArea; private IWorkingArea workingArea;
private IEnumerable<Action> StartupOperations private IEnumerable<IOperation> startupOperations;
{
get
{
yield return HandleCommandLineArguments;
yield return DetectOperatingSystem;
yield return EstablishWcfServiceConnection;
yield return DeactivateWindowsFeatures;
yield return InitializeProcessMonitoring;
yield return InitializeWorkingArea;
yield return InitializeTaskbar;
yield return InitializeBrowser;
yield return FinishInitialization;
}
}
public StartupController( public StartupController(
IApplicationController browserController, IApplicationController browserController,
@ -76,33 +63,78 @@ namespace SafeExamBrowser.Core.Behaviour
this.workingArea = workingArea; this.workingArea = workingArea;
} }
public bool TryInitializeApplication() public bool TryInitializeApplication(out Stack<IOperation> operations)
{ {
operations = new Stack<IOperation>();
try try
{ {
CreateStartupOperations();
InitializeApplicationLog(); InitializeApplicationLog();
InitializeSplashScreen(); InitializeSplashScreen();
foreach (var operation in StartupOperations) operations = PerformOperations();
{
operation();
splashScreen.UpdateProgress();
// TODO: Remove! FinishInitialization();
Thread.Sleep(250);
}
return true; return true;
} }
catch (Exception e) catch (Exception e)
{ {
logger.Error($"Failed to initialize application!", e); LogAndShowException(e);
messageBox.Show(text.Get(Key.MessageBox_StartupError), text.Get(Key.MessageBox_StartupErrorTitle), icon: MessageBoxIcon.Error); RevertOperations(operations);
FinishInitialization(false);
return false; return false;
} }
} }
private Stack<IOperation> PerformOperations()
{
var operations = new Stack<IOperation>();
foreach (var operation in startupOperations)
{
operations.Push(operation);
operation.SplashScreen = splashScreen;
operation.Perform();
splashScreen.Progress();
// TODO: Remove!
Thread.Sleep(250);
}
return operations;
}
private void RevertOperations(Stack<IOperation> operations)
{
while (operations.Any())
{
var operation = operations.Pop();
operation.Revert();
splashScreen.Regress();
// TODO: Remove!
Thread.Sleep(250);
}
}
private void CreateStartupOperations()
{
startupOperations = new IOperation[]
{
new ProcessMonitoringOperation(logger, processMonitor),
new WorkingAreaOperation(logger, processMonitor, taskbar, workingArea),
new TaskbarInitializationOperation(logger, aboutInfo, taskbar, uiFactory),
new BrowserInitializationOperation(browserController, browserInfo, logger, taskbar, uiFactory)
};
}
private void InitializeApplicationLog() private void InitializeApplicationLog()
{ {
var titleLine = $"/* {settings.ProgramTitle}, Version {settings.ProgramVersion}{Environment.NewLine}"; var titleLine = $"/* {settings.ProgramTitle}, Version {settings.ProgramVersion}{Environment.NewLine}";
@ -118,84 +150,29 @@ namespace SafeExamBrowser.Core.Behaviour
private void InitializeSplashScreen() private void InitializeSplashScreen()
{ {
splashScreen = uiFactory.CreateSplashScreen(settings, text); splashScreen = uiFactory.CreateSplashScreen(settings, text);
splashScreen.SetMaxProgress(StartupOperations.Count()); splashScreen.SetMaxProgress(startupOperations.Count());
splashScreen.UpdateText(Key.SplashScreen_StartupProcedure); splashScreen.UpdateText(Key.SplashScreen_StartupProcedure);
splashScreen.InvokeShow(); splashScreen.InvokeShow();
} }
private void HandleCommandLineArguments() private void LogAndShowException(Exception e)
{ {
// TODO logger.Error($"Failed to initialize application!", e);
messageBox.Show(text.Get(Key.MessageBox_StartupError), text.Get(Key.MessageBox_StartupErrorTitle), icon: MessageBoxIcon.Error);
logger.Info("Reverting operations...");
} }
private void DetectOperatingSystem() private void FinishInitialization(bool success = true)
{ {
// TODO if (success)
}
private void EstablishWcfServiceConnection()
{
// TODO
}
private void DeactivateWindowsFeatures()
{
// TODO
}
private void InitializeProcessMonitoring()
{
logger.Info("--- Initializing process monitoring ---");
splashScreen.UpdateText(Key.SplashScreen_InitializeProcessMonitoring);
// TODO
processMonitor.StartMonitoringExplorer();
}
private void InitializeWorkingArea()
{
logger.Info("--- Initializing working area ---");
splashScreen.UpdateText(Key.SplashScreen_WaitExplorerTermination, true);
processMonitor.CloseExplorerShell();
// TODO
// - Minimizing all open windows
// - Emptying clipboard
splashScreen.UpdateText(Key.SplashScreen_InitializeWorkingArea);
workingArea.InitializeFor(taskbar);
}
private void InitializeTaskbar()
{
logger.Info("--- Initializing taskbar ---");
splashScreen.UpdateText(Key.SplashScreen_InitializeTaskbar);
// TODO
var aboutNotification = uiFactory.CreateNotification(aboutInfo);
taskbar.AddNotification(aboutNotification);
}
private void InitializeBrowser()
{
logger.Info("--- Initializing browser ---");
splashScreen.UpdateText(Key.SplashScreen_InitializeBrowser);
// TODO
var browserButton = uiFactory.CreateApplicationButton(browserInfo);
browserController.RegisterApplicationButton(browserButton);
taskbar.AddButton(browserButton);
}
private void FinishInitialization()
{ {
logger.Info("--- Application successfully initialized! ---"); logger.Info("--- Application successfully initialized! ---");
splashScreen.InvokeClose(); splashScreen.InvokeClose();
} }
else
{
logger.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
}
} }
} }

View file

@ -40,6 +40,10 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Behaviour\Operations\BrowserInitializationOperation.cs" />
<Compile Include="Behaviour\Operations\ProcessMonitoringOperation.cs" />
<Compile Include="Behaviour\Operations\TaskbarInitializationOperation.cs" />
<Compile Include="Behaviour\Operations\WorkingAreaOperation.cs" />
<Compile Include="Behaviour\ShutdownController.cs" /> <Compile Include="Behaviour\ShutdownController.cs" />
<Compile Include="Behaviour\StartupController.cs" /> <Compile Include="Behaviour\StartupController.cs" />
<Compile Include="Logging\LogFileWriter.cs" /> <Compile Include="Logging\LogFileWriter.cs" />

View file

@ -22,7 +22,7 @@
<Image Grid.Column="0" Grid.ColumnSpan="2" Source="pack://application:,,,/SafeExamBrowser.UserInterface;component/Images/SplashScreen.png" /> <Image Grid.Column="0" Grid.ColumnSpan="2" Source="pack://application:,,,/SafeExamBrowser.UserInterface;component/Images/SplashScreen.png" />
<TextBlock x:Name="InfoTextBlock" Grid.Column="1" Foreground="White" Margin="10,75,10,10" TextWrapping="Wrap" /> <TextBlock x:Name="InfoTextBlock" Grid.Column="1" Foreground="White" Margin="10,75,10,10" TextWrapping="Wrap" />
</Grid> </Grid>
<ProgressBar x:Name="ProgressBar" Grid.Row="1" Minimum="0" Maximum="{Binding Path=MaxProgress}" Value="{Binding Path=CurrentProgress}" Background="#00000000" BorderThickness="0" /> <ProgressBar x:Name="ProgressBar" Grid.Row="1" Minimum="0" Maximum="{Binding Path=MaxProgress}" Value="{Binding Path=CurrentProgress}" IsIndeterminate="{Binding Path=IsIndeterminate}" Background="#00000000" BorderThickness="0" />
<TextBlock x:Name="StatusTextBlock" Grid.Row="1" Text="{Binding Path=Status}" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" /> <TextBlock x:Name="StatusTextBlock" Grid.Row="1" Text="{Binding Path=Status}" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" />
</Grid> </Grid>
</Border> </Border>

View file

@ -40,16 +40,26 @@ namespace SafeExamBrowser.UserInterface
Dispatcher.Invoke(Show); Dispatcher.Invoke(Show);
} }
public void Progress(int amount = 1)
{
model.CurrentProgress += amount;
}
public void Regress(int amount = 1)
{
model.CurrentProgress -= amount;
}
public void SetIndeterminate()
{
model.IsIndeterminate = true;
}
public void SetMaxProgress(int max) public void SetMaxProgress(int max)
{ {
model.MaxProgress = max; model.MaxProgress = max;
} }
public void UpdateProgress(int amount = 1)
{
model.CurrentProgress += amount;
}
public void UpdateText(Key key, bool showBusyIndication = false) public void UpdateText(Key key, bool showBusyIndication = false)
{ {
model.StopBusyIndication(); model.StopBusyIndication();

View file

@ -6,7 +6,7 @@
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Controls" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Controls"
xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" mc:Ignorable="d"
Title="Taskbar" Height="40" Width="750" WindowStyle="None" AllowsTransparency="True" Topmost="True" Icon="./Images/SafeExamBrowser.ico"> Title="Taskbar" Height="40" Width="750" WindowStyle="None" AllowsTransparency="True" Topmost="True" Visibility="Collapsed" Icon="./Images/SafeExamBrowser.ico">
<Window.Background> <Window.Background>
<SolidColorBrush Color="Black" Opacity="0.8" /> <SolidColorBrush Color="Black" Opacity="0.8" />
</Window.Background> </Window.Background>

View file

@ -14,6 +14,7 @@ namespace SafeExamBrowser.UserInterface.ViewModels
class SplashScreenViewModel : INotifyPropertyChanged class SplashScreenViewModel : INotifyPropertyChanged
{ {
private int currentProgress; private int currentProgress;
private bool isIndeterminate;
private int maxProgress; private int maxProgress;
private string status; private string status;
private Timer busyTimer; private Timer busyTimer;
@ -33,6 +34,19 @@ namespace SafeExamBrowser.UserInterface.ViewModels
} }
} }
public bool IsIndeterminate
{
get
{
return isIndeterminate;
}
set
{
isIndeterminate = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsIndeterminate)));
}
}
public int MaxProgress public int MaxProgress
{ {
get get

View file

@ -7,8 +7,10 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Windows; using System.Windows;
using SafeExamBrowser.Contracts.Behaviour;
namespace SafeExamBrowser namespace SafeExamBrowser
{ {
@ -57,12 +59,12 @@ namespace SafeExamBrowser
instances.BuildObjectGraph(); instances.BuildObjectGraph();
var success = instances.StartupController.TryInitializeApplication(); var success = instances.StartupController.TryInitializeApplication(out Stack<IOperation> operations);
if (success) if (success)
{ {
MainWindow = instances.Taskbar; MainWindow = instances.Taskbar;
MainWindow.Closing += (o, args) => ShutdownApplication(); MainWindow.Closing += (o, args) => ShutdownApplication(operations);
MainWindow.Show(); MainWindow.Show();
} }
else else
@ -71,10 +73,10 @@ namespace SafeExamBrowser
} }
} }
private void ShutdownApplication() private void ShutdownApplication(Stack<IOperation> operations)
{ {
MainWindow.Hide(); MainWindow.Hide();
instances.ShutdownController.FinalizeApplication(); instances.ShutdownController.FinalizeApplication(operations);
} }
} }
} }