1108 lines
44 KiB
C#
1108 lines
44 KiB
C#
#region BSD License
|
|
/*
|
|
*
|
|
* Original BSD 3-Clause License (https://github.com/ComponentFactory/Krypton/blob/master/LICENSE)
|
|
* © Component Factory Pty Ltd, 2006 - 2016, (Version 4.5.0.0) All rights reserved.
|
|
*
|
|
* New BSD 3-Clause License (https://github.com/Krypton-Suite/Standard-Toolkit/blob/master/LICENSE)
|
|
* Modifications by Peter Wagner(aka Wagnerp) & Simon Coghlan(aka Smurf-IV), et al. 2017 - 2023. All rights reserved.
|
|
*
|
|
*/
|
|
#endregion
|
|
|
|
// ReSharper disable InconsistentNaming
|
|
// ReSharper disable UnusedParameter.Local
|
|
using CloudBuilder.Gui.DependencyInjection;
|
|
using CloudBuilder.Gui.Policy;
|
|
using CloudBuilder.Gui.Properties;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Media;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using ContentAlignment = System.Drawing.ContentAlignment;
|
|
|
|
namespace Krypton.Toolkit
|
|
{
|
|
|
|
public partial class KryptonMessageBoxForm : KryptonForm
|
|
{
|
|
#region Static Fields
|
|
private const int GAP = 10;
|
|
private static readonly int OS_MAJOR_VERSION;
|
|
#endregion
|
|
|
|
#region Instance Fields
|
|
|
|
private readonly bool _showHelpButton;
|
|
private readonly string _text;
|
|
private readonly string _caption;
|
|
private readonly string _applicationPath;
|
|
private readonly KryptonMessageBoxButtons _buttons;
|
|
private readonly KryptonMessageBoxIcon _kryptonMessageBoxIcon;
|
|
private readonly Image? _applicationImage;
|
|
|
|
private readonly KryptonMessageBoxDefaultButton _defaultButton;
|
|
private readonly MessageBoxOptions _options; // https://github.com/Krypton-Suite/Standard-Toolkit/issues/313
|
|
|
|
// If help information provided or we are not a service/default desktop application then grab an owner for showing the message box
|
|
private readonly IWin32Window? _showOwner;
|
|
private readonly HelpInfo? _helpInfo;
|
|
private readonly KryptonMessageBoxNativeWindow _krtbNativeWindow;
|
|
|
|
// Action button features (aka _button5)
|
|
private readonly bool _showActionButton;
|
|
private readonly string _actionButtonText;
|
|
private readonly KryptonCommand? _actionButtonCommand;
|
|
|
|
// For the LinkLabel option
|
|
private readonly MessageBoxContentAreaType? _contentAreaType;
|
|
private readonly KryptonCommand? _linkLabelCommand;
|
|
private readonly ProcessStartInfo? _linkLaunchArgument;
|
|
private readonly ContentAlignment? _messageTextAlignment;
|
|
private readonly LinkArea _contentLinkArea;
|
|
|
|
#endregion
|
|
|
|
#region Identity
|
|
static KryptonMessageBoxForm() => OS_MAJOR_VERSION = Environment.OSVersion.Version.Major;
|
|
|
|
public KryptonMessageBoxForm()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
internal KryptonMessageBoxForm(IWin32Window? showOwner, string text, string caption,
|
|
KryptonMessageBoxButtons buttons,
|
|
KryptonMessageBoxIcon icon,
|
|
KryptonMessageBoxDefaultButton defaultButton,
|
|
MessageBoxOptions options,
|
|
HelpInfo? helpInfo, bool? showCtrlCopy,
|
|
bool? showHelpButton,
|
|
bool? showActionButton, string? actionButtonText,
|
|
KryptonCommand? actionButtonCommand,
|
|
Image? applicationImage,
|
|
string? applicationPath,
|
|
MessageBoxContentAreaType? contentAreaType,
|
|
KryptonCommand? linkLabelCommand,
|
|
ProcessStartInfo? linkLaunchArgument,
|
|
LinkArea? contentLinkArea)
|
|
{
|
|
// Store incoming values
|
|
_text = CommonHelper.NormalizeLineBreaks(text ?? string.Empty);
|
|
_caption = caption;
|
|
_buttons = buttons;
|
|
_kryptonMessageBoxIcon = icon;
|
|
_defaultButton = defaultButton;
|
|
_options = options;
|
|
_helpInfo = helpInfo;
|
|
_showOwner = showOwner;
|
|
_showHelpButton = showHelpButton ?? (helpInfo != null);
|
|
_showActionButton = showActionButton ?? false;
|
|
_actionButtonText = actionButtonText ?? string.Empty;
|
|
_actionButtonCommand = actionButtonCommand;
|
|
_applicationImage = applicationImage;
|
|
_applicationPath = applicationPath ?? string.Empty;
|
|
_contentAreaType = contentAreaType ?? MessageBoxContentAreaType.Normal;
|
|
_linkLabelCommand = linkLabelCommand ?? new KryptonCommand();
|
|
_contentLinkArea = contentLinkArea ?? new LinkArea(0, text.Length);
|
|
_linkLaunchArgument = linkLaunchArgument ?? new ProcessStartInfo();
|
|
_krtbNativeWindow = new();
|
|
|
|
|
|
|
|
// Create the form contents
|
|
InitializeComponent();
|
|
|
|
// Hookup the native window on the KRTB, only after IntializeComponent().
|
|
_krtbNativeWindow.AssignHandle(krtbMessageText.RichTextBox.Handle);
|
|
|
|
// Default Cursor for the KRTB when the cursors hovers over it
|
|
krtbMessageText.RichTextBox.Cursor = Cursors.Arrow;
|
|
|
|
RightToLeftLayout = _options.HasFlag(MessageBoxOptions.RtlReading);
|
|
|
|
// Update contents to match requirements
|
|
UpdateText();
|
|
UpdateIcon();
|
|
UpdateButtons();
|
|
UpdateDefault();
|
|
UpdateHelp();
|
|
UpdateTextExtra(showCtrlCopy);
|
|
UpdateContentAreaType(contentAreaType);
|
|
UpdateContentLinkArea(contentLinkArea);
|
|
|
|
if (!_text.Contains("\r\n"))
|
|
_panelContentArea.Padding = new Padding(0, 10, 0, 0);
|
|
else
|
|
_panelContentArea.Padding = new Padding(0, 0, 0, 0);
|
|
|
|
SetupActionButtonUI(_showActionButton);
|
|
|
|
// Finally calculate and set form sizing
|
|
UpdateSizing(showOwner);
|
|
}
|
|
|
|
#endregion Identity
|
|
|
|
#region Implementation
|
|
|
|
private void UpdateText()
|
|
{
|
|
Text = string.IsNullOrEmpty(_caption) ? string.Empty : _caption.Split(Environment.NewLine.ToCharArray())[0];
|
|
|
|
if (_contentAreaType == MessageBoxContentAreaType.Normal)
|
|
{
|
|
krtbMessageText.Text = _text;
|
|
|
|
krtbMessageText.RightToLeft = _options.HasFlag(MessageBoxOptions.RightAlign)
|
|
? RightToLeft.Yes
|
|
: _options.HasFlag(MessageBoxOptions.RtlReading)
|
|
? RightToLeft.Inherit
|
|
: RightToLeft.No;
|
|
}
|
|
else
|
|
{
|
|
_linkLabelMessageText.Text = _text;
|
|
|
|
_linkLabelMessageText.RightToLeft = _options.HasFlag(MessageBoxOptions.RightAlign) ? RightToLeft.Yes :
|
|
_options.HasFlag(MessageBoxOptions.RtlReading) ? RightToLeft.Inherit : RightToLeft.No;
|
|
}
|
|
}
|
|
|
|
private void UpdateTextExtra(bool? showCtrlCopy)
|
|
{
|
|
if (!showCtrlCopy.HasValue)
|
|
{
|
|
switch (_kryptonMessageBoxIcon)
|
|
{
|
|
case KryptonMessageBoxIcon.Error:
|
|
case KryptonMessageBoxIcon.Exclamation:
|
|
showCtrlCopy = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (showCtrlCopy == true)
|
|
{
|
|
TextExtra = @"Ctrl+c to copy";
|
|
}
|
|
}
|
|
|
|
private void UpdateIcon()
|
|
{
|
|
if (OSUtilities.IsWindowsEleven)
|
|
{
|
|
switch (_kryptonMessageBoxIcon)
|
|
{
|
|
case KryptonMessageBoxIcon.None:
|
|
// Windows XP and before will Beep, Vista and above do not!
|
|
if (OS_MAJOR_VERSION < 6)
|
|
{
|
|
SystemSounds.Beep.Play();
|
|
}
|
|
break;
|
|
case KryptonMessageBoxIcon.Hand:
|
|
_messageIcon.Image = Resources.GenericHand;
|
|
SystemSounds.Hand.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Question:
|
|
_messageIcon.Image = Resources.Question_Windows_11;
|
|
SystemSounds.Question.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Exclamation:
|
|
_messageIcon.Image = Resources.Warning_Windows_11;
|
|
SystemSounds.Exclamation.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Asterisk:
|
|
_messageIcon.Image = Resources.Asterisk_Windows_11;
|
|
SystemSounds.Asterisk.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Stop:
|
|
_messageIcon.Image = Resources.GenericStop;
|
|
SystemSounds.Asterisk.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Error:
|
|
_messageIcon.Image = Resources.Critical_Windows_11;
|
|
SystemSounds.Asterisk.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Warning:
|
|
_messageIcon.Image = Resources.Warning_Windows_11;
|
|
SystemSounds.Exclamation.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Information:
|
|
_messageIcon.Image = Resources.Information_Windows_11;
|
|
SystemSounds.Asterisk.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Shield:
|
|
_messageIcon.Image = Resources.UAC_Shield_Windows_11;
|
|
break;
|
|
case KryptonMessageBoxIcon.WindowsLogo:
|
|
_messageIcon.Image = Resources.Windows11;
|
|
break;
|
|
case KryptonMessageBoxIcon.Application:
|
|
if (_applicationImage != null)
|
|
{
|
|
_messageIcon.Image = _applicationImage;
|
|
}
|
|
else if (!string.IsNullOrEmpty(_applicationPath))
|
|
{
|
|
Image? sourceImage = GraphicsExtensions.ExtractIconFromFilePath(_applicationPath)?.ToBitmap();
|
|
Image? scaledImage = GraphicsExtensions.ScaleImage(sourceImage, new Size(32, 32));
|
|
|
|
_messageIcon.Image = scaledImage;
|
|
}
|
|
else
|
|
{
|
|
// Fall back to defaults
|
|
_messageIcon.Image = SystemIcons.Application.ToBitmap();
|
|
}
|
|
break;
|
|
case KryptonMessageBoxIcon.SystemApplication:
|
|
_messageIcon.Image = SystemIcons.Application.ToBitmap();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (_kryptonMessageBoxIcon)
|
|
{
|
|
case KryptonMessageBoxIcon.None:
|
|
// Windows XP and before will Beep, Vista and above do not!
|
|
if (OS_MAJOR_VERSION < 6)
|
|
{
|
|
SystemSounds.Beep.Play();
|
|
}
|
|
break;
|
|
case KryptonMessageBoxIcon.Hand:
|
|
_messageIcon.Image = Resources.GenericHand;
|
|
SystemSounds.Hand.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.SystemHand:
|
|
_messageIcon.Image = SystemIcons.Hand.ToBitmap();
|
|
SystemSounds.Hand.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Question:
|
|
_messageIcon.Image = Resources.GenericQuestion;
|
|
SystemSounds.Question.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.SystemQuestion:
|
|
_messageIcon.Image = SystemIcons.Question.ToBitmap();
|
|
SystemSounds.Question.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Exclamation:
|
|
_messageIcon.Image = Resources.GenericWarning;
|
|
SystemSounds.Exclamation.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.SystemExclamation:
|
|
_messageIcon.Image = SystemIcons.Warning.ToBitmap();
|
|
SystemSounds.Exclamation.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Asterisk:
|
|
_messageIcon.Image = OSUtilities.IsWindowsEleven
|
|
? Resources.Asterisk_Windows_11
|
|
: Resources.GenericAsterisk;
|
|
SystemSounds.Asterisk.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.SystemAsterisk:
|
|
_messageIcon.Image = SystemIcons.Asterisk.ToBitmap();
|
|
SystemSounds.Asterisk.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Stop:
|
|
_messageIcon.Image = Resources.GenericStop;
|
|
SystemSounds.Asterisk.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Error:
|
|
_messageIcon.Image = Resources.GenericCritical;
|
|
SystemSounds.Asterisk.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Warning:
|
|
_messageIcon.Image = Resources.GenericWarning;
|
|
SystemSounds.Exclamation.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Information:
|
|
_messageIcon.Image = Resources.GenericInformation;
|
|
SystemSounds.Asterisk.Play();
|
|
break;
|
|
case KryptonMessageBoxIcon.Shield:
|
|
if (OSUtilities.IsWindowsEleven)
|
|
{
|
|
_messageIcon.Image = Resources.UAC_Shield_Windows_11;
|
|
}
|
|
else if (OSUtilities.IsWindowsTen)
|
|
{
|
|
_messageIcon.Image = Resources.UAC_Shield_Windows_11;
|
|
}
|
|
else
|
|
{
|
|
_messageIcon.Image = Resources.UAC_Shield_Windows_11;
|
|
}
|
|
break;
|
|
case KryptonMessageBoxIcon.WindowsLogo:
|
|
// Because Windows 11 displays a generic application icon,
|
|
// we need to rely on a image instead
|
|
if (OSUtilities.IsWindowsEleven)
|
|
{
|
|
_messageIcon.Image = Resources.Windows11;
|
|
}
|
|
// Windows 10
|
|
else if (OSUtilities.IsWindowsTen)
|
|
{
|
|
_messageIcon.Image = Resources.Windows11;
|
|
}
|
|
else
|
|
{
|
|
_messageIcon.Image = SystemIcons.WinLogo.ToBitmap();
|
|
}
|
|
break;
|
|
case KryptonMessageBoxIcon.Application:
|
|
if (_applicationImage != null)
|
|
{
|
|
_messageIcon.Image = _applicationImage;
|
|
}
|
|
else if (!string.IsNullOrEmpty(_applicationPath))
|
|
{
|
|
Image? sourceImage = GraphicsExtensions.ExtractIconFromFilePath(_applicationPath)?.ToBitmap();
|
|
Image? scaledImage = GraphicsExtensions.ScaleImage(sourceImage, new Size(32, 32));
|
|
|
|
_messageIcon.Image = scaledImage;
|
|
}
|
|
else
|
|
{
|
|
// Fall back to defaults
|
|
_messageIcon.Image = SystemIcons.Application.ToBitmap();
|
|
}
|
|
break;
|
|
case KryptonMessageBoxIcon.SystemApplication:
|
|
_messageIcon.Image = SystemIcons.Application.ToBitmap();
|
|
break;
|
|
}
|
|
}
|
|
_messageIcon.Visible = (_kryptonMessageBoxIcon != KryptonMessageBoxIcon.None);
|
|
}
|
|
|
|
private void UpdateButtons()
|
|
{
|
|
switch (_buttons)
|
|
{
|
|
case KryptonMessageBoxButtons.OK:
|
|
_button1.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.OK);
|
|
_button1.DialogResult = DialogResult.OK;
|
|
_button1.Visible = true;
|
|
_button1.Enabled = true;
|
|
break;
|
|
case KryptonMessageBoxButtons.OKCancel:
|
|
_button1.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.OK);
|
|
_button2.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.CANCEL);
|
|
_button1.DialogResult = DialogResult.OK;
|
|
_button2.DialogResult = DialogResult.Cancel;
|
|
_button1.Visible = true;
|
|
_button1.Enabled = true;
|
|
_button2.Visible = true;
|
|
_button2.Enabled = true;
|
|
break;
|
|
case KryptonMessageBoxButtons.YesNo:
|
|
_button1.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.YES);
|
|
_button2.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.NO);
|
|
_button1.DialogResult = DialogResult.Yes;
|
|
_button2.DialogResult = DialogResult.No;
|
|
_button1.Visible = true;
|
|
_button1.Enabled = true;
|
|
_button2.Visible = true;
|
|
_button2.Enabled = true;
|
|
ControlBox = false;
|
|
break;
|
|
case KryptonMessageBoxButtons.YesNoCancel:
|
|
_button1.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.YES);
|
|
_button2.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.NO);
|
|
_button3.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.CANCEL);
|
|
_button1.DialogResult = DialogResult.Yes;
|
|
_button2.DialogResult = DialogResult.No;
|
|
_button3.DialogResult = DialogResult.Cancel;
|
|
_button1.Visible = true;
|
|
_button1.Enabled = true;
|
|
_button2.Visible = true;
|
|
_button2.Enabled = true;
|
|
_button3.Visible = true;
|
|
_button3.Enabled = true;
|
|
break;
|
|
case KryptonMessageBoxButtons.RetryCancel:
|
|
_button1.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.RETRY);
|
|
_button2.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.CANCEL);
|
|
_button1.DialogResult = DialogResult.Retry;
|
|
_button2.DialogResult = DialogResult.Cancel;
|
|
_button1.Visible = true;
|
|
_button1.Enabled = true;
|
|
_button2.Visible = true;
|
|
_button2.Enabled = true;
|
|
break;
|
|
case KryptonMessageBoxButtons.AbortRetryIgnore:
|
|
_button1.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.ABORT);
|
|
_button2.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.RETRY);
|
|
_button3.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.IGNORE);
|
|
_button1.DialogResult = DialogResult.Abort;
|
|
_button2.DialogResult = DialogResult.Retry;
|
|
_button3.DialogResult = DialogResult.Ignore;
|
|
_button1.Visible = true;
|
|
_button1.Enabled = true;
|
|
_button2.Visible = true;
|
|
_button2.Enabled = true;
|
|
_button3.Visible = true;
|
|
_button3.Enabled = true;
|
|
ControlBox = false;
|
|
break;
|
|
case KryptonMessageBoxButtons.CancelTryContinue:
|
|
_button1.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.CANCEL);
|
|
_button2.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.TRYAGAIN);
|
|
_button3.Text = GeneralToolkitStringsPolicy.GetInstance().ComposeMessage(GeneralToolkitStringsPolicy.CONTINUE);
|
|
_button1.DialogResult = DialogResult.Cancel;
|
|
#if NET6_0_OR_GREATER
|
|
_button2.DialogResult = DialogResult.TryAgain;
|
|
_button3.DialogResult = DialogResult.Continue;
|
|
#else
|
|
_button2.DialogResult = (DialogResult)10;
|
|
_button3.DialogResult = (DialogResult)11;
|
|
#endif
|
|
_button1.Visible = true;
|
|
_button1.Enabled = true;
|
|
_button2.Visible = true;
|
|
_button2.Enabled = true;
|
|
_button3.Visible = true;
|
|
_button3.Enabled = true;
|
|
break;
|
|
}
|
|
|
|
if (_showActionButton)
|
|
{
|
|
_button5.Text = _actionButtonText;
|
|
_button5.Visible = true;
|
|
_button5.Enabled = true;
|
|
_button5.KryptonCommand = _actionButtonCommand;
|
|
}
|
|
|
|
// Do we ignore the Alt+F4 on the buttons?
|
|
if (!ControlBox)
|
|
{
|
|
_button1.IgnoreAltF4 = true;
|
|
_button2.IgnoreAltF4 = true;
|
|
_button3.IgnoreAltF4 = true;
|
|
_button4.IgnoreAltF4 = true;
|
|
_button5.IgnoreAltF4 = true;
|
|
}
|
|
}
|
|
|
|
private void UpdateDefault()
|
|
{
|
|
switch (_defaultButton)
|
|
{
|
|
case KryptonMessageBoxDefaultButton.Button1:
|
|
_button1.Select();
|
|
AcceptButton = _button1;
|
|
break;
|
|
case KryptonMessageBoxDefaultButton.Button2:
|
|
_button2.Select();
|
|
AcceptButton = _button2;
|
|
break;
|
|
case KryptonMessageBoxDefaultButton.Button3:
|
|
_button3.Select();
|
|
AcceptButton = _button3;
|
|
break;
|
|
case KryptonMessageBoxDefaultButton.Button4:
|
|
_button4.Select();
|
|
AcceptButton = _showHelpButton ? _button4 : _button1;
|
|
break;
|
|
default:
|
|
_button1.Select();
|
|
AcceptButton = _showHelpButton ? _button4 : _button1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void UpdateHelp()
|
|
{
|
|
if (!_showHelpButton)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MessageButton helpButton = _buttons switch
|
|
{
|
|
KryptonMessageBoxButtons.OK => _button2,
|
|
KryptonMessageBoxButtons.OKCancel or KryptonMessageBoxButtons.YesNo or KryptonMessageBoxButtons.RetryCancel => _button3,
|
|
KryptonMessageBoxButtons.AbortRetryIgnore or KryptonMessageBoxButtons.YesNoCancel => _button4,
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
};
|
|
if (helpButton != null)
|
|
{
|
|
helpButton.Visible = true;
|
|
helpButton.Enabled = true;
|
|
helpButton.Text = KryptonLanguageManager.GeneralToolkitStrings.Help;
|
|
helpButton.KeyPress += (_, _) => LaunchHelp();
|
|
helpButton.Click += (_, _) => LaunchHelp();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// When the user clicks the Help button, the Help file specified in the helpFilePath parameter
|
|
/// is opened and the Help keyword topic identified by the keyword parameter is Displayed.
|
|
/// The form that owns the message box (or the active form) also receives the HelpRequested event.
|
|
/// </summary>
|
|
private void LaunchHelp()
|
|
{
|
|
try
|
|
{
|
|
if (_showOwner != null)
|
|
{
|
|
Control control = FromHandle(_showOwner.Handle);
|
|
|
|
MethodInfo? mInfoMethod = control.GetType().GetMethod(nameof(OnHelpRequested), BindingFlags.Instance | BindingFlags.NonPublic,
|
|
Type.DefaultBinder, new[] { typeof(HelpEventArgs) }, null)!;
|
|
mInfoMethod?.Invoke(control, new object[] { new HelpEventArgs(MousePosition) });
|
|
if (_helpInfo != null)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_helpInfo.HelpFilePath))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(_helpInfo.Keyword))
|
|
{
|
|
Help.ShowHelp(control, _helpInfo.HelpFilePath, _helpInfo.Keyword);
|
|
}
|
|
else
|
|
{
|
|
Help.ShowHelp(control, _helpInfo.HelpFilePath, _helpInfo.Navigator, _helpInfo.Param);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Do nothing if failure to send to Parent
|
|
}
|
|
}
|
|
|
|
private void UpdateSizing(IWin32Window? showOwner)
|
|
{
|
|
Size messageSizing = UpdateMessageSizing(showOwner);
|
|
Size buttonsSizing = UpdateButtonsSizing();
|
|
|
|
// Size of window is calculated from the client area
|
|
ClientSize = new Size(Math.Max(messageSizing.Width, buttonsSizing.Width),
|
|
messageSizing.Height + buttonsSizing.Height);
|
|
}
|
|
|
|
private Size UpdateMessageSizing(IWin32Window? showOwner)
|
|
{
|
|
// Update size of the message label but with a maximum width
|
|
Size textSize;
|
|
using (Graphics g = CreateGraphics())
|
|
{
|
|
// Find size of the label, with a max of 2/3 screen width
|
|
Screen screen = showOwner is IWin32Window window
|
|
? Screen.FromHandle(window.Handle)
|
|
: Screen.PrimaryScreen ?? throw new NullReferenceException("Screen.PrimaryScreen returned null");
|
|
|
|
Size scaledMonitorSize = screen.WorkingArea.Size;
|
|
scaledMonitorSize.Width = (int)(scaledMonitorSize.Width * 2 / 3.0f);
|
|
scaledMonitorSize.Height = (int)(scaledMonitorSize.Height * 0.95f);
|
|
|
|
Font textFont = krtbMessageText.StateCommon.Content.GetContentShortTextFont(PaletteState.Normal);
|
|
Font captionFont = KryptonManager.CurrentGlobalPalette!.BaseFont;
|
|
|
|
// Measure the string
|
|
SizeF messageSize = g.MeasureString(_text, textFont, new SizeF(scaledMonitorSize.Width, scaledMonitorSize.Height));
|
|
// SKC: Don't forget to add the TextExtra into the calculation
|
|
SizeF captionSize = TextRenderer.MeasureText($"{_caption} {TextExtra}", captionFont, scaledMonitorSize);
|
|
|
|
var messageXSize = Math.Max(messageSize.Width, captionSize.Width);
|
|
// Work out DPI adjustment factor
|
|
var factorX = g.DpiX > 96 ? (1.0f * g.DpiX / 96) : 1.0f;
|
|
var factorY = g.DpiY > 96 ? (1.0f * g.DpiY / 96) : 1.0f;
|
|
messageSize.Width = messageXSize * factorX;
|
|
messageSize.Height *= factorY;
|
|
|
|
textSize = Size.Ceiling(messageSize);
|
|
}
|
|
|
|
// Calculate the size of the icon area and text area including margins
|
|
Padding textPadding = krtbMessageText.StateCommon.Content.GetContentPadding(PaletteState.Normal);
|
|
Padding textAreaAllMargin = Padding.Add(textPadding, _panelContentArea.Margin);
|
|
|
|
Size iconArea = new Size(_messageIcon.Width + _messageIcon.Margin.Left + _messageIcon.Margin.Right,
|
|
_messageIcon.Height + _messageIcon.Margin.Top + _messageIcon.Margin.Bottom);
|
|
|
|
Size textArea = new Size(textSize.Width + textAreaAllMargin.Left + textAreaAllMargin.Right,
|
|
textSize.Height + textAreaAllMargin.Top + textAreaAllMargin.Bottom);
|
|
|
|
return new Size(textArea.Width + iconArea.Width,
|
|
Math.Max(iconArea.Height, textArea.Height));
|
|
}
|
|
|
|
private Size UpdateButtonsSizing()
|
|
{
|
|
var numButtons = 1;
|
|
|
|
// Button1 is always visible
|
|
Size button1Size = _button1.GetPreferredSize(Size.Empty);
|
|
var maxButtonSize = button1Size with { Width = button1Size.Width + GAP };
|
|
|
|
// If Button2 is visible
|
|
if (_button2.Enabled)
|
|
{
|
|
numButtons++;
|
|
Size button2Size = _button2.GetPreferredSize(Size.Empty);
|
|
maxButtonSize.Width = Math.Max(maxButtonSize.Width, button2Size.Width + GAP);
|
|
maxButtonSize.Height = Math.Max(maxButtonSize.Height, button2Size.Height);
|
|
}
|
|
|
|
// If Button3 is visible
|
|
if (_button3.Enabled)
|
|
{
|
|
numButtons++;
|
|
Size button3Size = _button3.GetPreferredSize(Size.Empty);
|
|
maxButtonSize.Width = Math.Max(maxButtonSize.Width, button3Size.Width + GAP);
|
|
maxButtonSize.Height = Math.Max(maxButtonSize.Height, button3Size.Height);
|
|
}
|
|
// If Button4 is visible
|
|
if (_button4.Enabled)
|
|
{
|
|
numButtons++;
|
|
Size button4Size = _button4.GetPreferredSize(Size.Empty);
|
|
maxButtonSize.Width = Math.Max(maxButtonSize.Width, button4Size.Width + GAP);
|
|
maxButtonSize.Height = Math.Max(maxButtonSize.Height, button4Size.Height);
|
|
}
|
|
|
|
// If Action button is visible
|
|
if (_button5.Enabled)
|
|
{
|
|
numButtons++;
|
|
Size actionButtonSize = _button5.GetPreferredSize(Size.Empty);
|
|
maxButtonSize.Width = Math.Max(maxButtonSize.Width, actionButtonSize.Width + GAP);
|
|
maxButtonSize.Height = Math.Max(maxButtonSize.Height, actionButtonSize.Height);
|
|
}
|
|
|
|
// Start positioning buttons 10 pixels from right edge
|
|
var right = _panelButtons.Right - GAP;
|
|
var left = _panelButtons.Left - GAP;
|
|
|
|
// If Action button is visible
|
|
if (_button5.Enabled)
|
|
{
|
|
_button5.Location = new Point(left - maxButtonSize.Width, GAP);
|
|
_button5.Size = maxButtonSize;
|
|
left -= maxButtonSize.Width + GAP;
|
|
}
|
|
|
|
// If Button4 is visible
|
|
if (_button4.Enabled)
|
|
{
|
|
_button4.Location = new Point(right - maxButtonSize.Width, GAP);
|
|
_button4.Size = maxButtonSize;
|
|
right -= maxButtonSize.Width + GAP;
|
|
}
|
|
|
|
// If Button3 is visible
|
|
if (_button3.Enabled)
|
|
{
|
|
_button3.Location = new Point(right - maxButtonSize.Width, GAP);
|
|
_button3.Size = maxButtonSize;
|
|
right -= maxButtonSize.Width + GAP;
|
|
}
|
|
|
|
// If Button2 is visible
|
|
if (_button2.Enabled)
|
|
{
|
|
_button2.Location = new Point(right - maxButtonSize.Width, GAP);
|
|
_button2.Size = maxButtonSize;
|
|
right -= maxButtonSize.Width + GAP;
|
|
}
|
|
|
|
// Button1 is always visible
|
|
_button1.Location = new Point(right - maxButtonSize.Width, GAP);
|
|
_button1.Size = maxButtonSize;
|
|
|
|
// Size the panel for the buttons
|
|
_panelButtons.Size = new Size((maxButtonSize.Width * numButtons) + (GAP * (numButtons + 1)), maxButtonSize.Height + (GAP * 2));
|
|
|
|
// Button area is the number of buttons with gaps between them and 10 pixels around all edges
|
|
return new Size((maxButtonSize.Width * numButtons) + (GAP * (numButtons + 1)), maxButtonSize.Height + (GAP * 2));
|
|
}
|
|
|
|
private void OnFormClosed(object sender, FormClosedEventArgs e)
|
|
{
|
|
_krtbNativeWindow.ReleaseHandle();
|
|
}
|
|
|
|
private void AnyKeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
// Escape key kills the dialog if we allow it to be closed
|
|
if (ControlBox
|
|
&& (e.KeyCode == Keys.Escape)
|
|
)
|
|
{
|
|
Close();
|
|
}
|
|
else if (e.KeyData == (Keys.Control | Keys.C))
|
|
{
|
|
const string DIVIDER = @"---------------------------";
|
|
const string BUTTON_TEXT_SPACER = @" ";
|
|
// Pressing Ctrl+C should copy message text into the clipboard
|
|
var sb = new StringBuilder();
|
|
|
|
sb.AppendLine(DIVIDER);
|
|
sb.AppendLine(Text);
|
|
sb.AppendLine(DIVIDER);
|
|
sb.AppendLine(krtbMessageText.Text);
|
|
sb.AppendLine(DIVIDER);
|
|
sb.Append(_button1.Text).Append(BUTTON_TEXT_SPACER);
|
|
if (_button2.Enabled)
|
|
{
|
|
sb.Append(_button2.Text).Append(BUTTON_TEXT_SPACER);
|
|
if (_button3.Enabled)
|
|
{
|
|
sb.Append(_button3.Text).Append(BUTTON_TEXT_SPACER);
|
|
}
|
|
|
|
if (_button4.Enabled)
|
|
{
|
|
sb.Append(_button4.Text).Append(BUTTON_TEXT_SPACER);
|
|
}
|
|
}
|
|
|
|
sb.AppendLine(string.Empty);
|
|
sb.AppendLine(DIVIDER);
|
|
|
|
Clipboard.SetText(sb.ToString(), TextDataFormat.Text);
|
|
Clipboard.SetText(sb.ToString(), TextDataFormat.UnicodeText);
|
|
}
|
|
}
|
|
|
|
/// <summary>Setups the action button UI.</summary>
|
|
/// <param name="visible">if set to <c>true</c> [visible].</param>
|
|
private void SetupActionButtonUI(bool visible)
|
|
{
|
|
_button5.Visible = visible;
|
|
_button5.Enabled = visible;
|
|
_button5.Click += (sender, args) =>
|
|
{
|
|
try
|
|
{
|
|
_actionButtonCommand?.PerformExecute();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.Assert(true, e.StackTrace);
|
|
|
|
DialogResult = DialogResult.None;
|
|
}
|
|
};
|
|
}
|
|
|
|
private void LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (_linkLabelCommand != null)
|
|
{
|
|
_linkLabelCommand.PerformExecute();
|
|
}
|
|
else if (_linkLaunchArgument != null)
|
|
{
|
|
Process.Start(_linkLaunchArgument);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
ExceptionHandler.CaptureException(exception);
|
|
}
|
|
}
|
|
|
|
/// <summary>Updates the type of the content area.</summary>
|
|
/// <param name="contentAreaType">Type of the content area.</param>
|
|
private void UpdateContentAreaType(MessageBoxContentAreaType? contentAreaType)
|
|
{
|
|
switch (contentAreaType)
|
|
{
|
|
case MessageBoxContentAreaType.Normal:
|
|
_linkLabelMessageText.Visible = false;
|
|
|
|
krtbMessageText.Visible = true;
|
|
break;
|
|
case MessageBoxContentAreaType.LinkLabel:
|
|
_linkLabelMessageText.Visible = true;
|
|
|
|
krtbMessageText.Visible = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void UpdateContentLinkArea(LinkArea? contentLinkArea)
|
|
{
|
|
if (contentLinkArea != null)
|
|
{
|
|
_linkLabelMessageText.LinkArea = (LinkArea)contentLinkArea;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#region Types
|
|
internal class HelpInfo
|
|
{
|
|
#region Identity
|
|
|
|
/// <summary>
|
|
/// Initialize a new instance of the HelpInfo class.
|
|
/// </summary>
|
|
/// <param name="helpFilePath">Value for HelpFilePath.</param>
|
|
/// <param name="keyword">Value for Keyword</param>
|
|
public HelpInfo(string? helpFilePath = null, string? keyword = null)
|
|
: this(helpFilePath, keyword, !string.IsNullOrWhiteSpace(keyword) ? HelpNavigator.Topic : HelpNavigator.TableOfContents, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize a new instance of the HelpInfo class.
|
|
/// </summary>
|
|
/// <param name="helpFilePath">Value for HelpFilePath.</param>
|
|
/// <param name="navigator">Value for Navigator</param>
|
|
/// <param name="param"></param>
|
|
public HelpInfo(string? helpFilePath, HelpNavigator navigator, object? param = null)
|
|
: this(helpFilePath, null, navigator, param)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize a new instance of the HelpInfo class.
|
|
/// </summary>
|
|
/// <param name="helpFilePath">Value for HelpFilePath.</param>
|
|
/// <param name="navigator">Value for Navigator</param>
|
|
/// <param name="keyword">Value for Keyword</param>
|
|
/// <param name="param"></param>
|
|
private HelpInfo(string? helpFilePath, string? keyword, HelpNavigator navigator, object? param)
|
|
{
|
|
HelpFilePath = helpFilePath ?? string.Empty;
|
|
Keyword = keyword ?? string.Empty;
|
|
Navigator = navigator;
|
|
Param = param;
|
|
}
|
|
#endregion
|
|
|
|
#region Properties
|
|
/// <summary>
|
|
/// Gets the HelpFilePath property.
|
|
/// </summary>
|
|
public string HelpFilePath { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the Keyword property.
|
|
/// </summary>
|
|
public string Keyword { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the Navigator property.
|
|
/// </summary>
|
|
public HelpNavigator Navigator { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the Param property.
|
|
/// </summary>
|
|
public object? Param { get; }
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
[ToolboxItem(false)]
|
|
[DesignTimeVisible(false)]
|
|
internal class MessageButton : KryptonButton
|
|
{
|
|
#region Identity
|
|
public MessageButton()
|
|
{
|
|
IgnoreAltF4 = false;
|
|
Visible = false;
|
|
Enabled = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets and sets the ignoring of Alt+F4
|
|
/// </summary>
|
|
public bool IgnoreAltF4 { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region Protected
|
|
/// <summary>
|
|
/// Processes Windows messages.
|
|
/// </summary>
|
|
/// <param name="m">The Windows Message to process. </param>
|
|
protected override void WndProc(ref Message m)
|
|
{
|
|
switch (m.Msg)
|
|
{
|
|
case KEYDOWN:
|
|
case SYSKEYDOWN:
|
|
if (IgnoreAltF4)
|
|
{
|
|
// Extract the keys being pressed
|
|
var keys = (Keys)(int)m.WParam.ToInt64();
|
|
|
|
// If the user standard combination ALT + F4
|
|
if ((keys == Keys.F4) && ((ModifierKeys & Keys.Alt) == Keys.Alt))
|
|
{
|
|
// Eat the message, so standard window proc does not close the window
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
base.WndProc(ref m);
|
|
}
|
|
|
|
public const int KEYDOWN = 256;
|
|
|
|
public const int SYSKEYDOWN = 260;
|
|
#endregion
|
|
}
|
|
|
|
internal class KryptonMessageBoxNativeWindow : NativeWindow
|
|
{
|
|
private const int WM_SETFOCUS = 7;
|
|
|
|
private const int WM_KILLFOCUS = 8;
|
|
|
|
private const int WM_MOUSEWHEEL = 522;
|
|
|
|
protected override void WndProc(ref Message m)
|
|
{
|
|
if (m.Msg == 7)
|
|
{
|
|
m.Msg = 8;
|
|
}
|
|
|
|
if (m.Msg != 522)
|
|
{
|
|
base.WndProc(ref m);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class ExceptionHandler
|
|
{
|
|
//
|
|
// 摘要:
|
|
// Initializes a new instance of the Krypton.Toolkit.ExceptionHandler class.
|
|
public ExceptionHandler()
|
|
{
|
|
}
|
|
|
|
//
|
|
// 摘要:
|
|
// Captures the exception.
|
|
//
|
|
// 參數:
|
|
// exception:
|
|
// The exception.
|
|
//
|
|
// title:
|
|
// The title.
|
|
//
|
|
// buttons:
|
|
// The buttons.
|
|
//
|
|
// icon:
|
|
// The icon.
|
|
//
|
|
// className:
|
|
// Name of the class.
|
|
//
|
|
// methodSignature:
|
|
// The method signature.
|
|
public static void CaptureException(Exception exception, string title = "Exception Caught", KryptonMessageBoxButtons buttons = KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon icon = KryptonMessageBoxIcon.Warning, string className = "", string methodSignature = "")
|
|
{
|
|
if (className != "")
|
|
{
|
|
KryptonMessageBox.Show($"An unexpected error has occurred: {exception.Message}.\n\nError in class: '{className}.cs'.", title, buttons, icon);
|
|
}
|
|
else if (methodSignature != "")
|
|
{
|
|
KryptonMessageBox.Show($"An unexpected error has occurred: {exception.Message}.\n\nError in method: '{methodSignature}'.", title, buttons, icon);
|
|
}
|
|
else if (className != "" && methodSignature != "")
|
|
{
|
|
KryptonMessageBox.Show($"An unexpected error has occurred: {exception.Message}.\n\nError in class: '{className}.cs'.\n\nError in method: '{methodSignature}'.", title, buttons, icon);
|
|
}
|
|
else
|
|
{
|
|
KryptonMessageBox.Show("An unexpected error has occurred: " + exception.Message + ".", title, buttons, icon);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 摘要:
|
|
// Captures a stack trace of the exception.
|
|
//
|
|
// 參數:
|
|
// exception:
|
|
// The incoming exception.
|
|
//
|
|
// fileName:
|
|
// The file to write the exception stack trace to.
|
|
public static void PrintStackTrace(Exception exception, string fileName)
|
|
{
|
|
try
|
|
{
|
|
if (!File.Exists(fileName))
|
|
{
|
|
File.Create(fileName);
|
|
}
|
|
|
|
StreamWriter streamWriter = new StreamWriter(fileName);
|
|
streamWriter.Write(exception.ToString());
|
|
streamWriter.Close();
|
|
streamWriter.Dispose();
|
|
}
|
|
catch (Exception exception2)
|
|
{
|
|
CaptureException(exception2);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 摘要:
|
|
// Captures a stack trace of the exception.
|
|
//
|
|
// 參數:
|
|
// exception:
|
|
// The incoming exception.
|
|
//
|
|
// fileName:
|
|
// The file to write the exception stack trace to.
|
|
public static void PrintExceptionStackTrace(Exception exception, string fileName)
|
|
{
|
|
try
|
|
{
|
|
if (!File.Exists(fileName))
|
|
{
|
|
File.Create(fileName);
|
|
}
|
|
|
|
StreamWriter streamWriter = new StreamWriter(fileName);
|
|
streamWriter.Write(exception.StackTrace);
|
|
streamWriter.Close();
|
|
streamWriter.Dispose();
|
|
}
|
|
catch (Exception exception2)
|
|
{
|
|
CaptureException(exception2);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|