SEBWIN-308, SEBWIN-312: Replaced BCL folder dialog with custom implementation.

This commit is contained in:
dbuechel 2020-01-22 16:08:57 +01:00
parent 97f3fb4a02
commit c1aa080f87
21 changed files with 56 additions and 234 deletions

View file

@ -217,7 +217,7 @@ namespace SafeExamBrowser.Browser
private void DialogHandler_DialogRequested(DialogRequestedEventArgs args)
{
var result = fileSystemDialog.Show(args.Element, args.InitialPath, args.Operation, title: args.Title, owner: window);
var result = fileSystemDialog.Show(args.Element, args.Operation, args.InitialPath, title: args.Title, owner: window);
if (result.Success)
{

View file

@ -29,6 +29,7 @@ using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
@ -48,6 +49,7 @@ namespace SafeExamBrowser.Client.UnitTests
private ClientContext context;
private Mock<IDisplayMonitor> displayMonitor;
private Mock<IExplorerShell> explorerShell;
private Mock<IFileSystemDialog> fileSystemDialog;
private Mock<IHashAlgorithm> hashAlgorithm;
private Mock<ILogger> logger;
private Mock<IMessageBox> messageBox;
@ -73,6 +75,7 @@ namespace SafeExamBrowser.Client.UnitTests
context = new ClientContext();
displayMonitor = new Mock<IDisplayMonitor>();
explorerShell = new Mock<IExplorerShell>();
fileSystemDialog = new Mock<IFileSystemDialog>();
hashAlgorithm = new Mock<IHashAlgorithm>();
logger = new Mock<ILogger>();
messageBox = new Mock<IMessageBox>();
@ -95,6 +98,7 @@ namespace SafeExamBrowser.Client.UnitTests
context,
displayMonitor.Object,
explorerShell.Object,
fileSystemDialog.Object,
hashAlgorithm.Object,
logger.Object,
messageBox.Object,
@ -396,17 +400,21 @@ namespace SafeExamBrowser.Client.UnitTests
public void Operations_MustAskForApplicationPath()
{
var args = new ApplicationNotFoundEventArgs(default(string), default(string));
var dialog = new Mock<IFolderDialog>();
var result = new FolderDialogResult { FolderPath = @"C:\Some\random\path\", Success = true };
var result = new FileSystemDialogResult { FullPath = @"C:\Some\random\path\", Success = true };
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(result);
fileSystemDialog.Setup(d => d.Show(
It.IsAny<FileSystemElement>(),
It.IsAny<FileSystemOperation>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<IWindow>())).Returns(result);
text.SetReturnsDefault(string.Empty);
uiFactory.Setup(f => f.CreateFolderDialog(It.IsAny<string>())).Returns(dialog.Object);
sut.TryStart();
operationSequence.Raise(s => s.ActionRequired += null, args);
Assert.AreEqual(result.FolderPath, args.CustomPath);
Assert.AreEqual(result.FullPath, args.CustomPath);
Assert.IsTrue(args.Success);
}
@ -414,12 +422,16 @@ namespace SafeExamBrowser.Client.UnitTests
public void Operations_MustAbortAskingForApplicationPath()
{
var args = new ApplicationNotFoundEventArgs(default(string), default(string));
var dialog = new Mock<IFolderDialog>();
var result = new FolderDialogResult { Success = false };
var result = new FileSystemDialogResult { Success = false };
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(result);
fileSystemDialog.Setup(d => d.Show(
It.IsAny<FileSystemElement>(),
It.IsAny<FileSystemOperation>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<IWindow>())).Returns(result);
text.SetReturnsDefault(string.Empty);
uiFactory.Setup(f => f.CreateFolderDialog(It.IsAny<string>())).Returns(dialog.Object);
sut.TryStart();
operationSequence.Raise(s => s.ActionRequired += null, args);

View file

@ -28,6 +28,7 @@ using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
@ -43,6 +44,7 @@ namespace SafeExamBrowser.Client
private ClientContext context;
private IDisplayMonitor displayMonitor;
private IExplorerShell explorerShell;
private IFileSystemDialog fileSystemDialog;
private IHashAlgorithm hashAlgorithm;
private ILogger logger;
private IMessageBox messageBox;
@ -64,6 +66,7 @@ namespace SafeExamBrowser.Client
ClientContext context,
IDisplayMonitor displayMonitor,
IExplorerShell explorerShell,
IFileSystemDialog fileSystemDialog,
IHashAlgorithm hashAlgorithm,
ILogger logger,
IMessageBox messageBox,
@ -79,6 +82,7 @@ namespace SafeExamBrowser.Client
this.context = context;
this.displayMonitor = displayMonitor;
this.explorerShell = explorerShell;
this.fileSystemDialog = fileSystemDialog;
this.hashAlgorithm = hashAlgorithm;
this.logger = logger;
this.messageBox = messageBox;
@ -535,12 +539,11 @@ namespace SafeExamBrowser.Client
private void AskForApplicationPath(ApplicationNotFoundEventArgs args)
{
var message = text.Get(TextKey.FolderDialog_ApplicationLocation).Replace("%%NAME%%", args.DisplayName).Replace("%%EXECUTABLE%%", args.ExecutableName);
var dialog = uiFactory.CreateFolderDialog(message);
var result = dialog.Show(splashScreen);
var result = fileSystemDialog.Show(FileSystemElement.Folder, FileSystemOperation.Open, message: message, owner: splashScreen);
if (result.Success)
{
args.CustomPath = result.FolderPath;
args.CustomPath = result.FullPath;
args.Success = true;
}
}

View file

@ -103,6 +103,7 @@ namespace SafeExamBrowser.Client
var applicationFactory = new ApplicationFactory(applicationMonitor, ModuleLogger(nameof(ApplicationFactory)), nativeMethods, processFactory);
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
var fileSystemDialog = BuildFileSystemDialog();
var hashAlgorithm = new HashAlgorithm();
var operations = new Queue<IOperation>();
@ -129,6 +130,7 @@ namespace SafeExamBrowser.Client
context,
displayMonitor,
explorerShell,
fileSystemDialog,
hashAlgorithm,
logger,
messageBox,

View file

@ -15,6 +15,6 @@ namespace SafeExamBrowser.UserInterface.Contracts.FileSystemDialog
/// <summary>
/// Creates a dialog according to the given parameters and shows it to the user.
/// </summary>
FileSystemDialogResult Show(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string), IWindow owner = default(IWindow));
FileSystemDialogResult Show(FileSystemElement element, FileSystemOperation operation, string initialPath = default(string), string message = default(string), string title = default(string), IWindow owner = default(IWindow));
}
}

View file

@ -49,11 +49,6 @@ namespace SafeExamBrowser.UserInterface.Contracts
/// </summary>
IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow);
/// <summary>
/// Creates a folder dialog with the given message.
/// </summary>
IFolderDialog CreateFolderDialog(string message);
/// <summary>
/// Creates a system control which allows to change the keyboard layout of the computer.
/// </summary>

View file

@ -85,11 +85,9 @@
<Compile Include="Shell\ITaskviewActivator.cs" />
<Compile Include="Shell\ITerminationActivator.cs" />
<Compile Include="Shell\Location.cs" />
<Compile Include="Windows\Data\FolderDialogResult.cs" />
<Compile Include="Windows\Data\LockScreenOption.cs" />
<Compile Include="Windows\Data\LockScreenResult.cs" />
<Compile Include="Windows\Events\WindowClosingEventHandler.cs" />
<Compile Include="Windows\IFolderDialog.cs" />
<Compile Include="Windows\ILockScreen.cs" />
<Compile Include="Windows\IPasswordDialog.cs" />
<Compile Include="Windows\Data\PasswordDialogResult.cs" />

View file

@ -1,26 +0,0 @@
/*
* Copyright (c) 2020 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/.
*/
namespace SafeExamBrowser.UserInterface.Contracts.Windows.Data
{
/// <summary>
/// Defines the user interaction result of an <see cref="IFolderDialog"/>.
/// </summary>
public class FolderDialogResult
{
/// <summary>
/// The full path of the folder selected by the user, or <c>null</c> if the interaction was unsuccessful.
/// </summary>
public string FolderPath { get; set; }
/// <summary>
/// Indicates whether the user confirmed the dialog or not.
/// </summary>
public bool Success { get; set; }
}
}

View file

@ -1,20 +0,0 @@
/*
* Copyright (c) 2020 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.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Contracts.Windows
{
public interface IFolderDialog
{
/// <summary>
/// Shows the dialog to the user. If a parent window is specified, the dialog is rendered modally for the given parent.
/// </summary>
FolderDialogResult Show(IWindow parent = null);
}
}

View file

@ -21,10 +21,14 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<WrapPanel Grid.Row="0" Margin="20,10">
<fa:ImageAwesome Name="OperationIcon" Foreground="LightGray" Height="25" Icon="FileOutline"/>
<TextBlock Name="Message" Margin="10,0,0,0" VerticalAlignment="Center" />
</WrapPanel>
<Grid Grid.Row="0" Margin="20,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<fa:ImageAwesome Grid.Column="0" Name="OperationIcon" Foreground="LightGray" Height="25" Icon="FileOutline" />
<TextBlock Grid.Column="1" Name="Message" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Center" />
</Grid>
<TreeView Grid.Row="1" Name="FileSystem" Margin="10,0" TreeViewItem.Expanded="FileSystem_Expanded" />
<TextBlock Grid.Row="2" Name="SelectedElement" Margin="10,5,10,5" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
<Grid Grid.Row="3" Name="NewElement" Margin="10,0,10,10">

View file

@ -34,9 +34,9 @@ namespace SafeExamBrowser.UserInterface.Desktop
public FileSystemDialog(
FileSystemElement element,
string initialPath,
FileSystemOperation operation,
IText text,
string initialPath = default(string),
string message = default(string),
string title = default(string),
IWindow parent = default(IWindow))
@ -242,7 +242,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
var header = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(2) };
var image = new Image
{
Height = 20,
Height = 16,
Source = IconLoader.LoadIconFor(file)
};
var item = new TreeViewItem();

View file

@ -22,15 +22,15 @@ namespace SafeExamBrowser.UserInterface.Desktop
this.text = text;
}
public FileSystemDialogResult Show(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = null, string title = null, IWindow owner = null)
public FileSystemDialogResult Show(FileSystemElement element, FileSystemOperation operation, string initialPath = default(string), string message = null, string title = null, IWindow owner = null)
{
if (owner is Window window)
{
return window.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title, owner).Show());
return window.Dispatcher.Invoke(() => new FileSystemDialog(element, operation, text, initialPath, message, title, owner).Show());
}
else
{
return new FileSystemDialog(element, initialPath, operation, text, message, title).Show();
return new FileSystemDialog(element, operation, text, initialPath, message, title).Show();
}
}
}

View file

@ -1,69 +0,0 @@
/*
* Copyright (c) 2020 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 System;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Desktop
{
internal class FolderDialog : IFolderDialog
{
private string message;
internal FolderDialog(string message)
{
this.message = message;
}
public FolderDialogResult Show(IWindow parent = null)
{
var result = new FolderDialogResult();
using (var dialog = new FolderBrowserDialog())
{
var dialogResult = DialogResult.None;
dialog.Description = message;
dialog.ShowNewFolderButton = false;
if (parent is Window w)
{
dialogResult = dialog.ShowDialog(new Win32Window(w));
}
else
{
dialogResult = dialog.ShowDialog();
}
if (dialogResult == DialogResult.OK)
{
result.FolderPath = dialog.SelectedPath;
result.Success = true;
}
}
return result;
}
private class Win32Window : System.Windows.Forms.IWin32Window
{
private Window w;
public Win32Window(Window w)
{
this.w = w;
}
public IntPtr Handle => w.Dispatcher.Invoke(() => new WindowInteropHelper(w).Handle);
}
}
}

View file

@ -152,7 +152,6 @@
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
</Compile>
<Compile Include="FileSystemDialogFactory.cs" />
<Compile Include="FolderDialog.cs" />
<Compile Include="LockScreen.xaml.cs">
<DependentUpon>LockScreen.xaml</DependentUpon>
</Compile>

View file

@ -75,11 +75,6 @@ namespace SafeExamBrowser.UserInterface.Desktop
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
}
public IFolderDialog CreateFolderDialog(string message)
{
return new FolderDialog(message);
}
public ISystemControl CreateKeyboardLayoutControl(IKeyboard keyboard, Location location)
{
if (location == Location.ActionCenter)

View file

@ -21,10 +21,14 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<WrapPanel Grid.Row="0" Margin="25,10">
<fa:ImageAwesome Name="OperationIcon" Foreground="LightGray" Height="35" Icon="FileOutline"/>
<TextBlock Name="Message" Margin="10,0,0,0" VerticalAlignment="Center" />
</WrapPanel>
<Grid Grid.Row="0" Margin="25,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<fa:ImageAwesome Grid.Column="0" Name="OperationIcon" Foreground="LightGray" Height="35" Icon="FileOutline" />
<TextBlock Grid.Column="1" Name="Message" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Center" />
</Grid>
<TreeView Grid.Row="1" Name="FileSystem" Margin="10,0" TreeViewItem.Expanded="FileSystem_Expanded" />
<TextBlock Grid.Row="2" Name="SelectedElement" Margin="10,5,10,5" Padding="0,8" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
<Grid Grid.Row="3" Name="NewElement" Margin="10,0,10,10">

View file

@ -34,9 +34,9 @@ namespace SafeExamBrowser.UserInterface.Mobile
public FileSystemDialog(
FileSystemElement element,
string initialPath,
FileSystemOperation operation,
IText text,
string initialPath = default(string),
string message = default(string),
string title = default(string),
IWindow parent = default(IWindow))

View file

@ -22,15 +22,15 @@ namespace SafeExamBrowser.UserInterface.Mobile
this.text = text;
}
public FileSystemDialogResult Show(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = null, string title = null, IWindow owner = null)
public FileSystemDialogResult Show(FileSystemElement element, FileSystemOperation operation, string initialPath = default(string), string message = null, string title = null, IWindow owner = null)
{
if (owner is Window window)
{
return window.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title, owner).Show());
return window.Dispatcher.Invoke(() => new FileSystemDialog(element, operation, text, initialPath, message, title, owner).Show());
}
else
{
return new FileSystemDialog(element, initialPath, operation, text, message, title).Show();
return new FileSystemDialog(element, operation, text, initialPath, message, title).Show();
}
}
}

View file

@ -1,69 +0,0 @@
/*
* Copyright (c) 2020 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 System;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Mobile
{
internal class FolderDialog : IFolderDialog
{
private string message;
internal FolderDialog(string message)
{
this.message = message;
}
public FolderDialogResult Show(IWindow parent = null)
{
var result = new FolderDialogResult();
using (var dialog = new FolderBrowserDialog())
{
var dialogResult = DialogResult.None;
dialog.Description = message;
dialog.ShowNewFolderButton = false;
if (parent is Window w)
{
dialogResult = dialog.ShowDialog(new Win32Window(w));
}
else
{
dialogResult = dialog.ShowDialog();
}
if (dialogResult == DialogResult.OK)
{
result.FolderPath = dialog.SelectedPath;
result.Success = true;
}
}
return result;
}
private class Win32Window : System.Windows.Forms.IWin32Window
{
private Window w;
public Win32Window(Window w)
{
this.w = w;
}
public IntPtr Handle => w.Dispatcher.Invoke(() => new WindowInteropHelper(w).Handle);
}
}
}

View file

@ -153,7 +153,6 @@
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
</Compile>
<Compile Include="FileSystemDialogFactory.cs" />
<Compile Include="FolderDialog.cs" />
<Compile Include="LockScreen.xaml.cs">
<DependentUpon>LockScreen.xaml</DependentUpon>
</Compile>

View file

@ -75,11 +75,6 @@ namespace SafeExamBrowser.UserInterface.Mobile
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
}
public IFolderDialog CreateFolderDialog(string message)
{
return new FolderDialog(message);
}
public ISystemControl CreateKeyboardLayoutControl(IKeyboard keyboard, Location location)
{
if (location == Location.ActionCenter)