SEBWIN-450: Completed basic implementation of proctoring window.
This commit is contained in:
parent
a3a5d42f98
commit
31a16caa87
15 changed files with 205 additions and 28 deletions
|
@ -16,6 +16,7 @@ using SafeExamBrowser.Settings;
|
||||||
using SafeExamBrowser.Settings.Browser;
|
using SafeExamBrowser.Settings.Browser;
|
||||||
using SafeExamBrowser.Settings.Browser.Proxy;
|
using SafeExamBrowser.Settings.Browser.Proxy;
|
||||||
using SafeExamBrowser.Settings.Logging;
|
using SafeExamBrowser.Settings.Logging;
|
||||||
|
using SafeExamBrowser.Settings.Proctoring;
|
||||||
using SafeExamBrowser.Settings.Security;
|
using SafeExamBrowser.Settings.Security;
|
||||||
using SafeExamBrowser.Settings.Service;
|
using SafeExamBrowser.Settings.Service;
|
||||||
using SafeExamBrowser.Settings.UserInterface;
|
using SafeExamBrowser.Settings.UserInterface;
|
||||||
|
@ -174,6 +175,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||||
settings.Mouse.AllowMiddleButton = false;
|
settings.Mouse.AllowMiddleButton = false;
|
||||||
settings.Mouse.AllowRightButton = true;
|
settings.Mouse.AllowRightButton = true;
|
||||||
|
|
||||||
|
settings.Proctoring.Enabled = false;
|
||||||
|
settings.Proctoring.WindowVisibility = WindowVisibility.Hidden;
|
||||||
|
|
||||||
settings.Security.AllowApplicationLogAccess = false;
|
settings.Security.AllowApplicationLogAccess = false;
|
||||||
settings.Security.AllowTermination = true;
|
settings.Security.AllowTermination = true;
|
||||||
settings.Security.AllowReconfiguration = false;
|
settings.Security.AllowReconfiguration = false;
|
||||||
|
|
|
@ -216,6 +216,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||||
|
|
||||||
internal static class Proctoring
|
internal static class Proctoring
|
||||||
{
|
{
|
||||||
|
internal const string WindowVisibility = "remoteProctoringViewShow";
|
||||||
|
|
||||||
internal static class JitsiMeet
|
internal static class JitsiMeet
|
||||||
{
|
{
|
||||||
internal const string Enabled = "jitsiMeetEnable";
|
internal const string Enabled = "jitsiMeetEnable";
|
||||||
|
|
|
@ -9,12 +9,23 @@
|
||||||
var domain = "%%_DOMAIN_%%";
|
var domain = "%%_DOMAIN_%%";
|
||||||
var options = {
|
var options = {
|
||||||
configOverwrite: {
|
configOverwrite: {
|
||||||
|
disable1On1Mode: true,
|
||||||
startAudioOnly: false,
|
startAudioOnly: false,
|
||||||
startWithAudioMuted: true,
|
startWithAudioMuted: true,
|
||||||
startWithVideoMuted: false,
|
startWithVideoMuted: false
|
||||||
disable1On1Mode: true
|
|
||||||
},
|
},
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
interfaceConfigOverwrite: {
|
||||||
|
JITSI_WATERMARK_LINK: '',
|
||||||
|
SHOW_JITSI_WATERMARK: false,
|
||||||
|
TOOLBAR_BUTTONS: [
|
||||||
|
'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||||
|
'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||||
|
'livestreaming', 'etherpad', /*'sharedvideo',*/ 'settings', 'raisehand',
|
||||||
|
'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||||
|
'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
|
||||||
|
]
|
||||||
|
},
|
||||||
jwt: "%%_TOKEN_%%",
|
jwt: "%%_TOKEN_%%",
|
||||||
parentNode: document.querySelector('#placeholder'),
|
parentNode: document.querySelector('#placeholder'),
|
||||||
roomName: "%%_ROOM_NAME_%%",
|
roomName: "%%_ROOM_NAME_%%",
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Microsoft.Web.WebView2.Core;
|
||||||
using Microsoft.Web.WebView2.Wpf;
|
using Microsoft.Web.WebView2.Wpf;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
|
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Proctoring
|
namespace SafeExamBrowser.Proctoring
|
||||||
{
|
{
|
||||||
|
@ -17,12 +18,34 @@ namespace SafeExamBrowser.Proctoring
|
||||||
{
|
{
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
|
|
||||||
|
public event FullScreenChangedEventHandler FullScreenChanged;
|
||||||
|
|
||||||
internal ProctoringControl(ILogger logger)
|
internal ProctoringControl(ILogger logger)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
CoreWebView2InitializationCompleted += ProctoringControl_CoreWebView2InitializationCompleted;
|
CoreWebView2InitializationCompleted += ProctoringControl_CoreWebView2InitializationCompleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ProctoringControl_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.IsSuccess)
|
||||||
|
{
|
||||||
|
CoreWebView2.ContainsFullScreenElementChanged += CoreWebView2_ContainsFullScreenElementChanged;
|
||||||
|
CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested;
|
||||||
|
logger.Info("Successfully initialized.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error("Failed to initialize!", e.InitializationException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CoreWebView2_ContainsFullScreenElementChanged(object sender, object e)
|
||||||
|
{
|
||||||
|
FullScreenChanged?.Invoke(CoreWebView2.ContainsFullScreenElement);
|
||||||
|
logger.Debug($"Full screen ${(CoreWebView2.ContainsFullScreenElement ? "activated" : "deactivated")}.");
|
||||||
|
}
|
||||||
|
|
||||||
private void CoreWebView2_PermissionRequested(object sender, CoreWebView2PermissionRequestedEventArgs e)
|
private void CoreWebView2_PermissionRequested(object sender, CoreWebView2PermissionRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PermissionKind == CoreWebView2PermissionKind.Camera || e.PermissionKind == CoreWebView2PermissionKind.Microphone)
|
if (e.PermissionKind == CoreWebView2PermissionKind.Camera || e.PermissionKind == CoreWebView2PermissionKind.Microphone)
|
||||||
|
@ -35,18 +58,5 @@ namespace SafeExamBrowser.Proctoring
|
||||||
logger.Info($"Denied access to {e.PermissionKind}.");
|
logger.Info($"Denied access to {e.PermissionKind}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProctoringControl_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.IsSuccess)
|
|
||||||
{
|
|
||||||
CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested;
|
|
||||||
logger.Info("Successfully initialized.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.Error("Failed to initialize!", e.InitializationException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace SafeExamBrowser.Proctoring
|
||||||
|
|
||||||
private string filePath;
|
private string filePath;
|
||||||
private IProctoringWindow window;
|
private IProctoringWindow window;
|
||||||
|
private ProctoringSettings settings;
|
||||||
|
|
||||||
public string Tooltip => "TODO!!!";
|
public string Tooltip => "TODO!!!";
|
||||||
public IconResource IconResource => new XamlIconResource();
|
public IconResource IconResource => new XamlIconResource();
|
||||||
|
@ -44,11 +45,20 @@ namespace SafeExamBrowser.Proctoring
|
||||||
|
|
||||||
public void Activate()
|
public void Activate()
|
||||||
{
|
{
|
||||||
window?.Show();
|
if (settings.WindowVisibility == WindowVisibility.Visible)
|
||||||
|
{
|
||||||
|
window?.BringToForeground();
|
||||||
|
}
|
||||||
|
else if (settings.WindowVisibility == WindowVisibility.AllowToHide || settings.WindowVisibility == WindowVisibility.AllowToShow)
|
||||||
|
{
|
||||||
|
window.Toggle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(ProctoringSettings settings)
|
public void Initialize(ProctoringSettings settings)
|
||||||
{
|
{
|
||||||
|
this.settings = settings;
|
||||||
|
|
||||||
if (settings.JitsiMeet.Enabled || settings.Zoom.Enabled)
|
if (settings.JitsiMeet.Enabled || settings.Zoom.Enabled)
|
||||||
{
|
{
|
||||||
var content = LoadContent(settings);
|
var content = LoadContent(settings);
|
||||||
|
@ -63,7 +73,12 @@ namespace SafeExamBrowser.Proctoring
|
||||||
});
|
});
|
||||||
|
|
||||||
window = uiFactory.CreateProctoringWindow(control);
|
window = uiFactory.CreateProctoringWindow(control);
|
||||||
window.Show();
|
|
||||||
|
if (settings.WindowVisibility == WindowVisibility.AllowToHide || settings.WindowVisibility == WindowVisibility.Visible)
|
||||||
|
{
|
||||||
|
window.SetTitle(settings.JitsiMeet.Enabled ? settings.JitsiMeet.Subject : settings.Zoom.UserName);
|
||||||
|
window.Show();
|
||||||
|
}
|
||||||
|
|
||||||
logger.Info($"Initialized proctoring with {(settings.JitsiMeet.Enabled ? "Jitsi Meet" : "Zoom")}.");
|
logger.Info($"Initialized proctoring with {(settings.JitsiMeet.Enabled ? "Jitsi Meet" : "Zoom")}.");
|
||||||
}
|
}
|
||||||
|
@ -73,9 +88,13 @@ namespace SafeExamBrowser.Proctoring
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Terminate()
|
void INotification.Terminate()
|
||||||
{
|
{
|
||||||
window?.Close();
|
window?.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IProctoringController.Terminate()
|
||||||
|
{
|
||||||
fileSystem.Delete(filePath);
|
fileSystem.Delete(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,11 @@ namespace SafeExamBrowser.Settings.Proctoring
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public JitsiMeetSettings JitsiMeet { get; set; }
|
public JitsiMeetSettings JitsiMeet { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines the visibility of the proctoring window.
|
||||||
|
/// </summary>
|
||||||
|
public WindowVisibility WindowVisibility { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All settings for remote proctoring with Zoom.
|
/// All settings for remote proctoring with Zoom.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
36
SafeExamBrowser.Settings/Proctoring/WindowVisibility.cs
Normal file
36
SafeExamBrowser.Settings/Proctoring/WindowVisibility.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.Settings.Proctoring
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines all possible visibility states for the proctoring window.
|
||||||
|
/// </summary>
|
||||||
|
public enum WindowVisibility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The proctoring window is hidden and cannot be shown by the user.
|
||||||
|
/// </summary>
|
||||||
|
Hidden,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The proctoring window is initially hidden but may be shown by the user.
|
||||||
|
/// </summary>
|
||||||
|
AllowToShow,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The proctoring window is initially visible but may be hidden by the user.
|
||||||
|
/// </summary>
|
||||||
|
AllowToHide,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The proctoring window is always visible and cannot be hidden by the user.
|
||||||
|
/// </summary>
|
||||||
|
Visible
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,6 +73,7 @@
|
||||||
<Compile Include="ConfigurationMode.cs" />
|
<Compile Include="ConfigurationMode.cs" />
|
||||||
<Compile Include="Proctoring\JitsiMeetSettings.cs" />
|
<Compile Include="Proctoring\JitsiMeetSettings.cs" />
|
||||||
<Compile Include="Proctoring\ProctoringSettings.cs" />
|
<Compile Include="Proctoring\ProctoringSettings.cs" />
|
||||||
|
<Compile Include="Proctoring\WindowVisibility.cs" />
|
||||||
<Compile Include="Proctoring\ZoomSettings.cs" />
|
<Compile Include="Proctoring\ZoomSettings.cs" />
|
||||||
<Compile Include="SessionMode.cs" />
|
<Compile Include="SessionMode.cs" />
|
||||||
<Compile Include="Security\KioskMode.cs" />
|
<Compile Include="Security\KioskMode.cs" />
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.Proctoring.Events
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the full screen state has changed.
|
||||||
|
/// </summary>
|
||||||
|
public delegate void FullScreenChangedEventHandler(bool fullScreen);
|
||||||
|
}
|
|
@ -6,13 +6,19 @@
|
||||||
* 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 SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
|
||||||
|
|
||||||
namespace SafeExamBrowser.UserInterface.Contracts.Proctoring
|
namespace SafeExamBrowser.UserInterface.Contracts.Proctoring
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Defines the functionality of a proctoring control, i.e. a web view running a WebRTC-enabled web application, which normally is embedded in
|
||||||
|
/// a <see cref="IProctoringWindow"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IProctoringControl
|
public interface IProctoringControl
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event fired when the full screen state changed.
|
||||||
|
/// </summary>
|
||||||
|
event FullScreenChangedEventHandler FullScreenChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,18 @@ using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
namespace SafeExamBrowser.UserInterface.Contracts.Proctoring
|
namespace SafeExamBrowser.UserInterface.Contracts.Proctoring
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Defines the functionality of a proctoring window.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IProctoringWindow : IWindow
|
public interface IProctoringWindow : IWindow
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the window title to the given value.
|
||||||
|
/// </summary>
|
||||||
|
void SetTitle(string title);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles the window visibility.
|
||||||
|
/// </summary>
|
||||||
|
void Toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
<Compile Include="FileSystemDialog\FileSystemElement.cs" />
|
<Compile Include="FileSystemDialog\FileSystemElement.cs" />
|
||||||
<Compile Include="FileSystemDialog\FileSystemOperation.cs" />
|
<Compile Include="FileSystemDialog\FileSystemOperation.cs" />
|
||||||
<Compile Include="FileSystemDialog\IFileSystemDialog.cs" />
|
<Compile Include="FileSystemDialog\IFileSystemDialog.cs" />
|
||||||
|
<Compile Include="Proctoring\Events\FullScreenChangedEventHandler.cs" />
|
||||||
<Compile Include="Proctoring\IProctoringControl.cs" />
|
<Compile Include="Proctoring\IProctoringControl.cs" />
|
||||||
<Compile Include="Proctoring\IProctoringWindow.cs" />
|
<Compile Include="Proctoring\IProctoringWindow.cs" />
|
||||||
<Compile Include="IProgressIndicator.cs" />
|
<Compile Include="IProgressIndicator.cs" />
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Windows"
|
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Windows"
|
||||||
mc:Ignorable="d" Height="250" Width="350" Topmost="True" WindowStyle="None">
|
mc:Ignorable="d" Height="250" Width="350" MinHeight="250" MinWidth="250" Topmost="True" WindowStyle="ToolWindow">
|
||||||
</Window>
|
</Window>
|
||||||
|
|
|
@ -11,6 +11,7 @@ using System.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
|
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
|
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
|
||||||
|
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||||
|
|
||||||
namespace SafeExamBrowser.UserInterface.Desktop.Windows
|
namespace SafeExamBrowser.UserInterface.Desktop.Windows
|
||||||
{
|
{
|
||||||
|
@ -47,6 +48,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
|
||||||
{
|
{
|
||||||
Dispatcher.Invoke(() =>
|
Dispatcher.Invoke(() =>
|
||||||
{
|
{
|
||||||
|
Closing -= ProctoringWindow_Closing;
|
||||||
closing?.Invoke();
|
closing?.Invoke();
|
||||||
base.Close();
|
base.Close();
|
||||||
});
|
});
|
||||||
|
@ -57,26 +59,67 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
|
||||||
Dispatcher.Invoke(base.Hide);
|
Dispatcher.Invoke(base.Hide);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetTitle(string title)
|
||||||
|
{
|
||||||
|
Dispatcher.Invoke(() => Title = title);
|
||||||
|
}
|
||||||
|
|
||||||
public new void Show()
|
public new void Show()
|
||||||
{
|
{
|
||||||
Dispatcher.Invoke(base.Show);
|
Dispatcher.Invoke(base.Show);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProctoringWindow_Closing(object sender, CancelEventArgs e)
|
public void Toggle()
|
||||||
{
|
{
|
||||||
closing?.Invoke();
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
if (Visibility == Visibility.Visible)
|
||||||
|
{
|
||||||
|
base.Hide();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base.Show();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeWindow(object control)
|
private void InitializeWindow(IProctoringControl control)
|
||||||
{
|
{
|
||||||
if (control is UIElement element)
|
if (control is UIElement element)
|
||||||
{
|
{
|
||||||
Content = element;
|
Content = element;
|
||||||
|
control.FullScreenChanged += Control_FullScreenChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
Closing += ProctoringWindow_Closing;
|
Closing += ProctoringWindow_Closing;
|
||||||
Top = SystemParameters.WorkArea.Height - Height;
|
Loaded += ProctoringWindow_Loaded;
|
||||||
Left = SystemParameters.WorkArea.Width - Width;
|
Top = SystemParameters.WorkArea.Height - Height - 15;
|
||||||
|
Left = SystemParameters.WorkArea.Width - Width - 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Control_FullScreenChanged(bool fullScreen)
|
||||||
|
{
|
||||||
|
if (fullScreen)
|
||||||
|
{
|
||||||
|
WindowState = WindowState.Maximized;
|
||||||
|
WindowStyle = WindowStyle.None;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WindowState = WindowState.Normal;
|
||||||
|
WindowStyle = WindowStyle.ToolWindow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProctoringWindow_Closing(object sender, CancelEventArgs e)
|
||||||
|
{
|
||||||
|
e.Cancel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProctoringWindow_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
this.HideCloseButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,12 @@ namespace SafeExamBrowser.UserInterface.Shared.Utilities
|
||||||
{
|
{
|
||||||
public static class WindowExtensions
|
public static class WindowExtensions
|
||||||
{
|
{
|
||||||
|
private const int GWL_STYLE = -16;
|
||||||
private const uint MF_BYCOMMAND = 0x00000000;
|
private const uint MF_BYCOMMAND = 0x00000000;
|
||||||
private const uint MF_GRAYED = 0x00000001;
|
private const uint MF_GRAYED = 0x00000001;
|
||||||
private const uint MF_ENABLED = 0x00000000;
|
private const uint MF_ENABLED = 0x00000000;
|
||||||
private const uint SC_CLOSE = 0xF060;
|
private const uint SC_CLOSE = 0xF060;
|
||||||
|
private const int WS_SYSMENU = 0x80000;
|
||||||
|
|
||||||
public static void DisableCloseButton(this Window window)
|
public static void DisableCloseButton(this Window window)
|
||||||
{
|
{
|
||||||
|
@ -31,10 +33,24 @@ namespace SafeExamBrowser.UserInterface.Shared.Utilities
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void HideCloseButton(this Window window)
|
||||||
|
{
|
||||||
|
var helper = new WindowInteropHelper(window);
|
||||||
|
var style = GetWindowLong(helper.Handle, GWL_STYLE) & ~WS_SYSMENU;
|
||||||
|
|
||||||
|
SetWindowLong(helper.Handle, GWL_STYLE, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
|
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
|
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue