Reduced left padding for the title and text labels, reduced right padding of buttons for better alignment, added code to resize the title panel to fix the close button position - https://puu.sh/KFOEG/5c3d72aaee.png
861 lines
41 KiB
C#
861 lines
41 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows.Forms;
|
|
|
|
namespace JR.Utils.GUI.Forms
|
|
{
|
|
public class FlexibleMessageBox
|
|
{
|
|
#region Public statics
|
|
|
|
public static double MAX_WIDTH_FACTOR = 0.7;
|
|
public static double MAX_HEIGHT_FACTOR = 0.9;
|
|
public static Font FONT = SystemFonts.MessageBoxFont;
|
|
|
|
#endregion
|
|
|
|
#region Public show functions
|
|
|
|
public static DialogResult Show(string text)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(owner, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(string text, string caption)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text, string caption)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(null, text, caption, buttons, icon, defaultButton);
|
|
}
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
|
{
|
|
return FlexibleMessageBoxForm.Show(owner, text, caption, buttons, icon, defaultButton);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal form class
|
|
|
|
private class FlexibleMessageBoxForm : Form
|
|
{
|
|
#region Constants and P/Invoke
|
|
|
|
private const int CS_DROPSHADOW = 0x00020000;
|
|
private const int WM_NCLBUTTONDOWN = 0xA1;
|
|
private const int HT_CAPTION = 0x2;
|
|
private const int BORDER_RADIUS = 12;
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool ReleaseCapture();
|
|
|
|
protected override CreateParams CreateParams
|
|
{
|
|
get
|
|
{
|
|
CreateParams cp = base.CreateParams;
|
|
cp.ClassStyle |= CS_DROPSHADOW;
|
|
return cp;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Form-Designer generated code
|
|
|
|
private System.ComponentModel.IContainer components = null;
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing && (components != null))
|
|
{
|
|
components.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
private void CloseButton_Click(object sender, EventArgs e)
|
|
{
|
|
this.DialogResult = DialogResult.Cancel;
|
|
this.Close();
|
|
}
|
|
|
|
private void TitlePanel_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
ReleaseCapture();
|
|
SendMessage(this.Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero);
|
|
}
|
|
}
|
|
|
|
private void InitializeComponent()
|
|
{
|
|
components = new System.ComponentModel.Container();
|
|
button1 = new System.Windows.Forms.Button();
|
|
richTextBoxMessage = new System.Windows.Forms.RichTextBox();
|
|
FlexibleMessageBoxFormBindingSource = new System.Windows.Forms.BindingSource(components);
|
|
panel1 = new System.Windows.Forms.Panel();
|
|
pictureBoxForIcon = new System.Windows.Forms.PictureBox();
|
|
button2 = new System.Windows.Forms.Button();
|
|
button3 = new System.Windows.Forms.Button();
|
|
titlePanel = new System.Windows.Forms.Panel();
|
|
titleLabel = new System.Windows.Forms.Label();
|
|
closeButton = new System.Windows.Forms.Button();
|
|
((System.ComponentModel.ISupportInitialize)FlexibleMessageBoxFormBindingSource).BeginInit();
|
|
panel1.SuspendLayout();
|
|
((System.ComponentModel.ISupportInitialize)pictureBoxForIcon).BeginInit();
|
|
titlePanel.SuspendLayout();
|
|
SuspendLayout();
|
|
//
|
|
// titlePanel
|
|
//
|
|
titlePanel.BackColor = System.Drawing.Color.FromArgb(20, 24, 29);
|
|
titlePanel.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
|
titlePanel.Location = new System.Drawing.Point(6, 6);
|
|
titlePanel.Name = "titlePanel";
|
|
titlePanel.Size = new System.Drawing.Size(248, 28);
|
|
titlePanel.TabIndex = 10;
|
|
titlePanel.Controls.Add(closeButton);
|
|
titlePanel.Controls.Add(titleLabel);
|
|
titlePanel.MouseDown += TitlePanel_MouseDown;
|
|
//
|
|
// titleLabel
|
|
//
|
|
titleLabel.AutoSize = false;
|
|
titleLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
|
titleLabel.ForeColor = System.Drawing.Color.White;
|
|
titleLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
|
|
titleLabel.Location = new System.Drawing.Point(0, 0);
|
|
titleLabel.Name = "titleLabel";
|
|
titleLabel.Padding = new System.Windows.Forms.Padding(12, 0, 0, 0);
|
|
titleLabel.Size = new System.Drawing.Size(218, 28);
|
|
titleLabel.TabIndex = 0;
|
|
titleLabel.Text = "<Caption>";
|
|
titleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
|
titleLabel.MouseDown += TitlePanel_MouseDown;
|
|
//
|
|
// closeButton
|
|
//
|
|
closeButton.Dock = System.Windows.Forms.DockStyle.Right;
|
|
closeButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
|
closeButton.FlatAppearance.BorderSize = 0;
|
|
closeButton.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(200, 60, 60);
|
|
closeButton.BackColor = System.Drawing.Color.FromArgb(20, 24, 29);
|
|
closeButton.ForeColor = System.Drawing.Color.White;
|
|
closeButton.Font = new System.Drawing.Font("Segoe UI", 9F);
|
|
closeButton.Location = new System.Drawing.Point(218, 0);
|
|
closeButton.Name = "closeButton";
|
|
closeButton.Size = new System.Drawing.Size(30, 28);
|
|
closeButton.TabIndex = 1;
|
|
closeButton.TabStop = false;
|
|
closeButton.Text = "✕";
|
|
closeButton.Click += CloseButton_Click;
|
|
//
|
|
// button1
|
|
//
|
|
button1.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
|
|
button1.DialogResult = System.Windows.Forms.DialogResult.OK;
|
|
button1.Location = new System.Drawing.Point(26, 80);
|
|
button1.Name = "button1";
|
|
button1.Size = new System.Drawing.Size(75, 28);
|
|
button1.TabIndex = 2;
|
|
button1.Text = "OK";
|
|
button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
|
button1.FlatAppearance.BorderSize = 0;
|
|
button1.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(60, 65, 80);
|
|
button1.BackColor = System.Drawing.Color.FromArgb(42, 45, 58);
|
|
button1.ForeColor = System.Drawing.Color.White;
|
|
button1.Font = new System.Drawing.Font("Segoe UI", 9F);
|
|
button1.Cursor = System.Windows.Forms.Cursors.Hand;
|
|
button1.Visible = false;
|
|
//
|
|
// richTextBoxMessage
|
|
//
|
|
richTextBoxMessage.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom
|
|
| System.Windows.Forms.AnchorStyles.Left
|
|
| System.Windows.Forms.AnchorStyles.Right;
|
|
richTextBoxMessage.BackColor = System.Drawing.Color.FromArgb(20, 24, 29);
|
|
richTextBoxMessage.ForeColor = System.Drawing.Color.White;
|
|
richTextBoxMessage.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
|
richTextBoxMessage.DataBindings.Add(new System.Windows.Forms.Binding("Text", FlexibleMessageBoxFormBindingSource, "MessageText", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
|
richTextBoxMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
|
|
richTextBoxMessage.Location = new System.Drawing.Point(46, 6);
|
|
richTextBoxMessage.Margin = new System.Windows.Forms.Padding(0);
|
|
richTextBoxMessage.Name = "richTextBoxMessage";
|
|
richTextBoxMessage.ReadOnly = true;
|
|
richTextBoxMessage.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical;
|
|
richTextBoxMessage.Size = new System.Drawing.Size(190, 20);
|
|
richTextBoxMessage.TabIndex = 0;
|
|
richTextBoxMessage.TabStop = false;
|
|
richTextBoxMessage.Text = "<Message>";
|
|
richTextBoxMessage.LinkClicked += new System.Windows.Forms.LinkClickedEventHandler(richTextBoxMessage_LinkClicked);
|
|
//
|
|
// panel1
|
|
//
|
|
panel1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom
|
|
| System.Windows.Forms.AnchorStyles.Left
|
|
| System.Windows.Forms.AnchorStyles.Right;
|
|
panel1.BackColor = System.Drawing.Color.FromArgb(20, 24, 29);
|
|
panel1.Controls.Add(pictureBoxForIcon);
|
|
panel1.Controls.Add(richTextBoxMessage);
|
|
panel1.Location = new System.Drawing.Point(6, 34);
|
|
panel1.Name = "panel1";
|
|
panel1.Size = new System.Drawing.Size(248, 59);
|
|
panel1.TabIndex = 1;
|
|
//
|
|
// pictureBoxForIcon
|
|
//
|
|
pictureBoxForIcon.BackColor = System.Drawing.Color.Transparent;
|
|
pictureBoxForIcon.Location = new System.Drawing.Point(15, 15);
|
|
pictureBoxForIcon.Name = "pictureBoxForIcon";
|
|
pictureBoxForIcon.Size = new System.Drawing.Size(32, 32);
|
|
pictureBoxForIcon.TabIndex = 8;
|
|
pictureBoxForIcon.TabStop = false;
|
|
//
|
|
// button2
|
|
//
|
|
button2.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
|
|
button2.DialogResult = System.Windows.Forms.DialogResult.OK;
|
|
button2.Location = new System.Drawing.Point(107, 80);
|
|
button2.Name = "button2";
|
|
button2.Size = new System.Drawing.Size(75, 28);
|
|
button2.TabIndex = 3;
|
|
button2.Text = "OK";
|
|
button2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
|
button2.FlatAppearance.BorderSize = 0;
|
|
button2.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(60, 65, 80);
|
|
button2.BackColor = System.Drawing.Color.FromArgb(42, 45, 58);
|
|
button2.ForeColor = System.Drawing.Color.White;
|
|
button2.Font = new System.Drawing.Font("Segoe UI", 9F);
|
|
button2.Cursor = System.Windows.Forms.Cursors.Hand;
|
|
button2.Visible = false;
|
|
//
|
|
// button3
|
|
//
|
|
button3.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
|
|
button3.DialogResult = System.Windows.Forms.DialogResult.OK;
|
|
button3.Location = new System.Drawing.Point(188, 80);
|
|
button3.Name = "button3";
|
|
button3.Size = new System.Drawing.Size(75, 28);
|
|
button3.TabIndex = 0;
|
|
button3.Text = "OK";
|
|
button3.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
|
button3.FlatAppearance.BorderSize = 0;
|
|
button3.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(60, 65, 80);
|
|
button3.BackColor = System.Drawing.Color.FromArgb(42, 45, 58);
|
|
button3.ForeColor = System.Drawing.Color.White;
|
|
button3.Font = new System.Drawing.Font("Segoe UI", 9F);
|
|
button3.Cursor = System.Windows.Forms.Cursors.Hand;
|
|
button3.Visible = false;
|
|
//
|
|
// FlexibleMessageBoxForm
|
|
//
|
|
AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
|
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
|
BackColor = System.Drawing.Color.FromArgb(25, 25, 30);
|
|
ForeColor = System.Drawing.Color.White;
|
|
FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
|
ClientSize = new System.Drawing.Size(260, 115);
|
|
Padding = new System.Windows.Forms.Padding(5);
|
|
Controls.Add(titlePanel);
|
|
Controls.Add(button3);
|
|
Controls.Add(button2);
|
|
Controls.Add(button1);
|
|
Controls.Add(panel1);
|
|
MaximizeBox = false;
|
|
MinimizeBox = false;
|
|
MinimumSize = new System.Drawing.Size(276, 120);
|
|
Name = "FlexibleMessageBoxForm";
|
|
ShowIcon = false;
|
|
SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
|
|
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
|
Text = "<Caption>";
|
|
Shown += new System.EventHandler(FlexibleMessageBoxForm_Shown);
|
|
((System.ComponentModel.ISupportInitialize)FlexibleMessageBoxFormBindingSource).EndInit();
|
|
panel1.ResumeLayout(false);
|
|
((System.ComponentModel.ISupportInitialize)pictureBoxForIcon).EndInit();
|
|
titlePanel.ResumeLayout(false);
|
|
ResumeLayout(false);
|
|
|
|
// Apply rounded corners and custom painting
|
|
this.Paint += FlexibleMessageBoxForm_Paint;
|
|
button1.Paint += RoundedButton_Paint;
|
|
button2.Paint += RoundedButton_Paint;
|
|
button3.Paint += RoundedButton_Paint;
|
|
|
|
// Setup hover effects for buttons
|
|
SetupButtonHover(button1);
|
|
SetupButtonHover(button2);
|
|
SetupButtonHover(button3);
|
|
|
|
Activate();
|
|
}
|
|
|
|
private Button button1;
|
|
private BindingSource FlexibleMessageBoxFormBindingSource;
|
|
private RichTextBox richTextBoxMessage;
|
|
private Panel panel1;
|
|
private PictureBox pictureBoxForIcon;
|
|
private Button button2;
|
|
private Button button3;
|
|
private Panel titlePanel;
|
|
private Label titleLabel;
|
|
private Button closeButton;
|
|
|
|
#endregion
|
|
|
|
#region Custom Painting
|
|
|
|
private Dictionary<Button, bool> _buttonHoverState = new Dictionary<Button, bool>();
|
|
private const int SHADOW_SIZE = 2;
|
|
private const int CONTENT_RADIUS = 10;
|
|
|
|
private void FlexibleMessageBoxForm_Paint(object sender, PaintEventArgs e)
|
|
{
|
|
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
|
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
|
|
|
int w = this.Width;
|
|
int h = this.Height;
|
|
|
|
// Draw shadow gradient layers around the content area
|
|
for (int i = SHADOW_SIZE; i >= 1; i--)
|
|
{
|
|
int alpha = (SHADOW_SIZE - i + 1) * 12; // 12, 24, 36, 48, 60
|
|
Rectangle shadowRect = new Rectangle(
|
|
SHADOW_SIZE - i,
|
|
SHADOW_SIZE - i,
|
|
w - (SHADOW_SIZE - i) * 2 - 1,
|
|
h - (SHADOW_SIZE - i) * 2 - 1);
|
|
|
|
using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 0, 0, 0), 1))
|
|
using (GraphicsPath shadowPath = GetRoundedRectPath(shadowRect, CONTENT_RADIUS + i))
|
|
{
|
|
e.Graphics.DrawPath(shadowPen, shadowPath);
|
|
}
|
|
}
|
|
|
|
// Draw content background
|
|
Rectangle contentRect = new Rectangle(SHADOW_SIZE, SHADOW_SIZE, w - SHADOW_SIZE * 2, h - SHADOW_SIZE * 2);
|
|
using (GraphicsPath contentPath = GetRoundedRectPath(contentRect, CONTENT_RADIUS))
|
|
{
|
|
using (SolidBrush bgBrush = new SolidBrush(Color.FromArgb(20, 24, 29)))
|
|
{
|
|
e.Graphics.FillPath(bgBrush, contentPath);
|
|
}
|
|
|
|
// Draw thin border
|
|
using (Pen borderPen = new Pen(Color.FromArgb(70, 80, 100), 1f))
|
|
{
|
|
e.Graphics.DrawPath(borderPen, contentPath);
|
|
}
|
|
}
|
|
|
|
// Apply rounded region to form (with shadow area)
|
|
using (GraphicsPath regionPath = GetRoundedRectPath(new Rectangle(0, 0, w, h), CONTENT_RADIUS + SHADOW_SIZE))
|
|
{
|
|
this.Region = new Region(regionPath);
|
|
}
|
|
}
|
|
|
|
private void SetupButtonHover(Button btn)
|
|
{
|
|
_buttonHoverState[btn] = false;
|
|
|
|
btn.MouseEnter += (s, e) =>
|
|
{
|
|
_buttonHoverState[btn] = true;
|
|
btn.Invalidate();
|
|
};
|
|
|
|
btn.MouseLeave += (s, e) =>
|
|
{
|
|
_buttonHoverState[btn] = false;
|
|
btn.Invalidate();
|
|
};
|
|
}
|
|
|
|
private void RoundedButton_Paint(object sender, PaintEventArgs e)
|
|
{
|
|
Button btn = sender as Button;
|
|
if (btn == null) return;
|
|
|
|
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
|
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
|
|
|
bool isHovered = _buttonHoverState.ContainsKey(btn) && _buttonHoverState[btn];
|
|
|
|
int radius = 4;
|
|
|
|
// Use a rect that's 1 pixel smaller to avoid edge clipping
|
|
Rectangle drawRect = new Rectangle(1, 1, btn.Width - 2, btn.Height - 2);
|
|
|
|
// Fill entire button area with parent background first to clear previous state
|
|
using (SolidBrush clearBrush = new SolidBrush(Color.FromArgb(20, 24, 29)))
|
|
{
|
|
e.Graphics.FillRectangle(clearBrush, 0, 0, btn.Width, btn.Height);
|
|
}
|
|
|
|
using (GraphicsPath path = GetRoundedRectPath(drawRect, radius))
|
|
{
|
|
// Determine colors based on hover state
|
|
Color bgColor = isHovered
|
|
? Color.FromArgb(93, 203, 173) // Accent color on hover
|
|
: btn.BackColor;
|
|
|
|
Color textColor = isHovered
|
|
? Color.FromArgb(20, 20, 20) // Dark text on accent
|
|
: btn.ForeColor;
|
|
|
|
// Draw background
|
|
using (SolidBrush brush = new SolidBrush(bgColor))
|
|
{
|
|
e.Graphics.FillPath(brush, path);
|
|
}
|
|
|
|
// Draw subtle border on normal state
|
|
if (!isHovered)
|
|
{
|
|
using (Pen borderPen = new Pen(Color.FromArgb(70, 75, 90), 1))
|
|
{
|
|
e.Graphics.DrawPath(borderPen, path);
|
|
}
|
|
}
|
|
|
|
// Draw text centered in original button bounds
|
|
TextRenderer.DrawText(e.Graphics, btn.Text, btn.Font,
|
|
new Rectangle(0, 0, btn.Width, btn.Height), textColor,
|
|
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
|
|
}
|
|
|
|
// Set region to full button size (not the draw rect)
|
|
using (GraphicsPath regionPath = GetRoundedRectPath(new Rectangle(0, 0, btn.Width, btn.Height), radius))
|
|
{
|
|
btn.Region = new Region(regionPath);
|
|
}
|
|
}
|
|
|
|
private GraphicsPath GetRoundedRectPath(Rectangle rect, int radius)
|
|
{
|
|
GraphicsPath path = new GraphicsPath();
|
|
|
|
if (radius <= 0)
|
|
{
|
|
path.AddRectangle(rect);
|
|
return path;
|
|
}
|
|
|
|
int diameter = radius * 2;
|
|
|
|
// Ensure diameter doesn't exceed rect dimensions
|
|
diameter = Math.Min(diameter, Math.Min(rect.Width, rect.Height));
|
|
radius = diameter / 2;
|
|
|
|
Rectangle arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
|
|
|
|
// Top left arc
|
|
path.AddArc(arcRect, 180, 90);
|
|
|
|
// Top right arc
|
|
arcRect.X = rect.Right - diameter;
|
|
path.AddArc(arcRect, 270, 90);
|
|
|
|
// Bottom right arc
|
|
arcRect.Y = rect.Bottom - diameter;
|
|
path.AddArc(arcRect, 0, 90);
|
|
|
|
// Bottom left arc
|
|
arcRect.X = rect.Left;
|
|
path.AddArc(arcRect, 90, 90);
|
|
|
|
path.CloseFigure();
|
|
return path;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private constants
|
|
|
|
private static readonly string STANDARD_MESSAGEBOX_SEPARATOR_LINES = "---------------------------\n";
|
|
private static readonly string STANDARD_MESSAGEBOX_SEPARATOR_SPACES = " ";
|
|
|
|
private enum ButtonID { OK = 0, CANCEL, YES, NO, ABORT, RETRY, IGNORE };
|
|
|
|
private enum TwoLetterISOLanguageID { en, de, es, it };
|
|
private static readonly string[] BUTTON_TEXTS_ENGLISH_EN = { "OK", "Cancel", "&Yes", "&No", "&Abort", "&Retry", "&Ignore" };
|
|
private static readonly string[] BUTTON_TEXTS_GERMAN_DE = { "OK", "Abbrechen", "&Ja", "&Nein", "&Abbrechen", "&Wiederholen", "&Ignorieren" };
|
|
private static readonly string[] BUTTON_TEXTS_SPANISH_ES = { "Aceptar", "Cancelar", "&Sí", "&No", "&Abortar", "&Reintentar", "&Ignorar" };
|
|
private static readonly string[] BUTTON_TEXTS_ITALIAN_IT = { "OK", "Annulla", "&Sì", "&No", "&Interrompi", "&Riprova", "&Ignora" };
|
|
|
|
#endregion
|
|
|
|
#region Private members
|
|
|
|
private MessageBoxDefaultButton defaultButton;
|
|
private int visibleButtonsCount;
|
|
private readonly TwoLetterISOLanguageID languageID = TwoLetterISOLanguageID.en;
|
|
|
|
#endregion
|
|
|
|
#region Private constructor
|
|
|
|
private FlexibleMessageBoxForm()
|
|
{
|
|
InitializeComponent();
|
|
_ = Enum.TryParse<TwoLetterISOLanguageID>(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, out languageID);
|
|
KeyPreview = true;
|
|
KeyUp += FlexibleMessageBoxForm_KeyUp;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private helper functions
|
|
|
|
private static string[] GetStringRows(string message)
|
|
{
|
|
if (string.IsNullOrEmpty(message))
|
|
{
|
|
return null;
|
|
}
|
|
return message.Split(new char[] { '\n' }, StringSplitOptions.None);
|
|
}
|
|
|
|
private string GetButtonText(ButtonID buttonID)
|
|
{
|
|
int buttonTextArrayIndex = Convert.ToInt32(buttonID);
|
|
switch (languageID)
|
|
{
|
|
case TwoLetterISOLanguageID.de: return BUTTON_TEXTS_GERMAN_DE[buttonTextArrayIndex];
|
|
case TwoLetterISOLanguageID.es: return BUTTON_TEXTS_SPANISH_ES[buttonTextArrayIndex];
|
|
case TwoLetterISOLanguageID.it: return BUTTON_TEXTS_ITALIAN_IT[buttonTextArrayIndex];
|
|
default: return BUTTON_TEXTS_ENGLISH_EN[buttonTextArrayIndex];
|
|
}
|
|
}
|
|
|
|
private static double GetCorrectedWorkingAreaFactor(double workingAreaFactor)
|
|
{
|
|
const double MIN_FACTOR = 0.2;
|
|
const double MAX_FACTOR = 1.0;
|
|
return workingAreaFactor < MIN_FACTOR ? MIN_FACTOR : workingAreaFactor > MAX_FACTOR ? MAX_FACTOR : workingAreaFactor;
|
|
}
|
|
|
|
private static void SetDialogStartPosition(FlexibleMessageBoxForm flexibleMessageBoxForm, IWin32Window owner)
|
|
{
|
|
flexibleMessageBoxForm.StartPosition = FormStartPosition.Manual;
|
|
|
|
// Try to get owner form, fallback to active form if owner is null
|
|
Form ownerForm = null;
|
|
if (owner != null)
|
|
{
|
|
ownerForm = owner as Form;
|
|
if (ownerForm == null)
|
|
{
|
|
Control ownerControl = Control.FromHandle(owner.Handle);
|
|
ownerForm = ownerControl?.FindForm();
|
|
}
|
|
}
|
|
|
|
// Fallback to active form if no owner specified
|
|
if (ownerForm == null)
|
|
{
|
|
ownerForm = Form.ActiveForm;
|
|
}
|
|
|
|
if (ownerForm != null && ownerForm.Visible)
|
|
{
|
|
// Center relative to owner window
|
|
int x = ownerForm.Left + (ownerForm.Width - flexibleMessageBoxForm.Width) / 2;
|
|
int y = ownerForm.Top + (ownerForm.Height - flexibleMessageBoxForm.Height) / 2;
|
|
|
|
// Ensure the dialog stays within screen bounds
|
|
Screen screen = Screen.FromControl(ownerForm);
|
|
x = Math.Max(screen.WorkingArea.Left, Math.Min(x, screen.WorkingArea.Right - flexibleMessageBoxForm.Width));
|
|
y = Math.Max(screen.WorkingArea.Top, Math.Min(y, screen.WorkingArea.Bottom - flexibleMessageBoxForm.Height));
|
|
|
|
flexibleMessageBoxForm.Left = x;
|
|
flexibleMessageBoxForm.Top = y;
|
|
}
|
|
else
|
|
{
|
|
// No owner found: center on current screen
|
|
CenterOnScreen(flexibleMessageBoxForm);
|
|
}
|
|
}
|
|
|
|
private static void CenterOnScreen(FlexibleMessageBoxForm form)
|
|
{
|
|
Screen screen = Screen.FromPoint(Cursor.Position);
|
|
form.Left = screen.WorkingArea.Left + (screen.WorkingArea.Width - form.Width) / 2;
|
|
form.Top = screen.WorkingArea.Top + (screen.WorkingArea.Height - form.Height) / 2;
|
|
}
|
|
|
|
private static void SetDialogSizes(FlexibleMessageBoxForm flexibleMessageBoxForm, string text, string caption)
|
|
{
|
|
flexibleMessageBoxForm.MaximumSize = new Size(Convert.ToInt32(SystemInformation.WorkingArea.Width * FlexibleMessageBoxForm.GetCorrectedWorkingAreaFactor(MAX_WIDTH_FACTOR)),
|
|
Convert.ToInt32(SystemInformation.WorkingArea.Height * FlexibleMessageBoxForm.GetCorrectedWorkingAreaFactor(MAX_HEIGHT_FACTOR)));
|
|
|
|
string[] stringRows = GetStringRows(text);
|
|
if (stringRows == null) return;
|
|
|
|
int textHeight = TextRenderer.MeasureText(text, FONT).Height;
|
|
const int SCROLLBAR_WIDTH_OFFSET = 15;
|
|
int longestTextRowWidth = stringRows.Max(textForRow => TextRenderer.MeasureText(textForRow, FONT).Width);
|
|
int captionWidth = TextRenderer.MeasureText(caption, SystemFonts.CaptionFont).Width;
|
|
int textWidth = Math.Max(longestTextRowWidth + SCROLLBAR_WIDTH_OFFSET, captionWidth);
|
|
|
|
int marginWidth = flexibleMessageBoxForm.Width - flexibleMessageBoxForm.richTextBoxMessage.Width;
|
|
int marginHeight = flexibleMessageBoxForm.Height - flexibleMessageBoxForm.richTextBoxMessage.Height;
|
|
|
|
flexibleMessageBoxForm.Size = new Size(textWidth + marginWidth, textHeight + marginHeight);
|
|
}
|
|
|
|
private static void SetDialogIcon(FlexibleMessageBoxForm flexibleMessageBoxForm, MessageBoxIcon icon)
|
|
{
|
|
switch (icon)
|
|
{
|
|
case MessageBoxIcon.Information:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Image = SystemIcons.Information.ToBitmap();
|
|
break;
|
|
case MessageBoxIcon.Warning:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Image = SystemIcons.Warning.ToBitmap();
|
|
break;
|
|
case MessageBoxIcon.Error:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Image = SystemIcons.Error.ToBitmap();
|
|
break;
|
|
case MessageBoxIcon.Question:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Image = SystemIcons.Question.ToBitmap();
|
|
break;
|
|
default:
|
|
flexibleMessageBoxForm.pictureBoxForIcon.Visible = false;
|
|
flexibleMessageBoxForm.richTextBoxMessage.Left -= flexibleMessageBoxForm.pictureBoxForIcon.Width;
|
|
flexibleMessageBoxForm.richTextBoxMessage.Width += flexibleMessageBoxForm.pictureBoxForIcon.Width;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void SetDialogButtons(FlexibleMessageBoxForm flexibleMessageBoxForm, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton)
|
|
{
|
|
switch (buttons)
|
|
{
|
|
case MessageBoxButtons.AbortRetryIgnore:
|
|
flexibleMessageBoxForm.visibleButtonsCount = 3;
|
|
flexibleMessageBoxForm.button1.Visible = true;
|
|
flexibleMessageBoxForm.button1.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.ABORT);
|
|
flexibleMessageBoxForm.button1.DialogResult = DialogResult.Abort;
|
|
flexibleMessageBoxForm.button2.Visible = true;
|
|
flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.RETRY);
|
|
flexibleMessageBoxForm.button2.DialogResult = DialogResult.Retry;
|
|
flexibleMessageBoxForm.button3.Visible = true;
|
|
flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.IGNORE);
|
|
flexibleMessageBoxForm.button3.DialogResult = DialogResult.Ignore;
|
|
flexibleMessageBoxForm.ControlBox = false;
|
|
break;
|
|
|
|
case MessageBoxButtons.OKCancel:
|
|
flexibleMessageBoxForm.visibleButtonsCount = 2;
|
|
flexibleMessageBoxForm.button2.Visible = true;
|
|
flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.OK);
|
|
flexibleMessageBoxForm.button2.DialogResult = DialogResult.OK;
|
|
flexibleMessageBoxForm.button3.Visible = true;
|
|
flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.CANCEL);
|
|
flexibleMessageBoxForm.button3.DialogResult = DialogResult.Cancel;
|
|
flexibleMessageBoxForm.CancelButton = flexibleMessageBoxForm.button3;
|
|
break;
|
|
|
|
case MessageBoxButtons.RetryCancel:
|
|
flexibleMessageBoxForm.visibleButtonsCount = 2;
|
|
flexibleMessageBoxForm.button2.Visible = true;
|
|
flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.RETRY);
|
|
flexibleMessageBoxForm.button2.DialogResult = DialogResult.Retry;
|
|
flexibleMessageBoxForm.button3.Visible = true;
|
|
flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.CANCEL);
|
|
flexibleMessageBoxForm.button3.DialogResult = DialogResult.Cancel;
|
|
flexibleMessageBoxForm.CancelButton = flexibleMessageBoxForm.button3;
|
|
break;
|
|
|
|
case MessageBoxButtons.YesNo:
|
|
flexibleMessageBoxForm.visibleButtonsCount = 2;
|
|
flexibleMessageBoxForm.button2.Visible = true;
|
|
flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.YES);
|
|
flexibleMessageBoxForm.button2.DialogResult = DialogResult.Yes;
|
|
flexibleMessageBoxForm.button3.Visible = true;
|
|
flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.NO);
|
|
flexibleMessageBoxForm.button3.DialogResult = DialogResult.No;
|
|
flexibleMessageBoxForm.ControlBox = false;
|
|
break;
|
|
|
|
case MessageBoxButtons.YesNoCancel:
|
|
flexibleMessageBoxForm.visibleButtonsCount = 3;
|
|
flexibleMessageBoxForm.button1.Visible = true;
|
|
flexibleMessageBoxForm.button1.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.YES);
|
|
flexibleMessageBoxForm.button1.DialogResult = DialogResult.Yes;
|
|
flexibleMessageBoxForm.button2.Visible = true;
|
|
flexibleMessageBoxForm.button2.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.NO);
|
|
flexibleMessageBoxForm.button2.DialogResult = DialogResult.No;
|
|
flexibleMessageBoxForm.button3.Visible = true;
|
|
flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.CANCEL);
|
|
flexibleMessageBoxForm.button3.DialogResult = DialogResult.Cancel;
|
|
flexibleMessageBoxForm.CancelButton = flexibleMessageBoxForm.button3;
|
|
break;
|
|
|
|
case MessageBoxButtons.OK:
|
|
default:
|
|
flexibleMessageBoxForm.visibleButtonsCount = 1;
|
|
flexibleMessageBoxForm.button3.Visible = true;
|
|
flexibleMessageBoxForm.button3.Text = flexibleMessageBoxForm.GetButtonText(ButtonID.OK);
|
|
flexibleMessageBoxForm.button3.DialogResult = DialogResult.OK;
|
|
flexibleMessageBoxForm.CancelButton = flexibleMessageBoxForm.button3;
|
|
break;
|
|
}
|
|
flexibleMessageBoxForm.defaultButton = defaultButton;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private event handlers
|
|
|
|
private void FlexibleMessageBoxForm_Shown(object sender, EventArgs e)
|
|
{
|
|
int buttonIndexToFocus;
|
|
switch (defaultButton)
|
|
{
|
|
case MessageBoxDefaultButton.Button1:
|
|
default:
|
|
buttonIndexToFocus = 1;
|
|
break;
|
|
case MessageBoxDefaultButton.Button2:
|
|
buttonIndexToFocus = 2;
|
|
break;
|
|
case MessageBoxDefaultButton.Button3:
|
|
buttonIndexToFocus = 3;
|
|
break;
|
|
}
|
|
|
|
if (buttonIndexToFocus > visibleButtonsCount)
|
|
buttonIndexToFocus = visibleButtonsCount;
|
|
|
|
Button buttonToFocus = buttonIndexToFocus == 3 ? button3 : buttonIndexToFocus == 2 ? button2 : button1;
|
|
_ = buttonToFocus.Focus();
|
|
}
|
|
|
|
private void richTextBoxMessage_LinkClicked(object sender, LinkClickedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
_ = Process.Start(e.LinkText);
|
|
}
|
|
finally
|
|
{
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
}
|
|
|
|
private void FlexibleMessageBoxForm_KeyUp(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.Control && (e.KeyCode == Keys.C || e.KeyCode == Keys.Insert))
|
|
{
|
|
string buttonsTextLine = (button1.Visible ? button1.Text + STANDARD_MESSAGEBOX_SEPARATOR_SPACES : string.Empty)
|
|
+ (button2.Visible ? button2.Text + STANDARD_MESSAGEBOX_SEPARATOR_SPACES : string.Empty)
|
|
+ (button3.Visible ? button3.Text + STANDARD_MESSAGEBOX_SEPARATOR_SPACES : string.Empty);
|
|
|
|
string textForClipboard = STANDARD_MESSAGEBOX_SEPARATOR_LINES
|
|
+ Text + Environment.NewLine
|
|
+ STANDARD_MESSAGEBOX_SEPARATOR_LINES
|
|
+ richTextBoxMessage.Text + Environment.NewLine
|
|
+ STANDARD_MESSAGEBOX_SEPARATOR_LINES
|
|
+ buttonsTextLine.Replace("&", string.Empty) + Environment.NewLine
|
|
+ STANDARD_MESSAGEBOX_SEPARATOR_LINES;
|
|
|
|
Clipboard.SetText(textForClipboard);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public string CaptionText { get; set; }
|
|
public string MessageText { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region Public show function
|
|
|
|
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
|
{
|
|
FlexibleMessageBoxForm flexibleMessageBoxForm = new FlexibleMessageBoxForm
|
|
{
|
|
ShowInTaskbar = false,
|
|
CaptionText = caption,
|
|
MessageText = text
|
|
};
|
|
flexibleMessageBoxForm.FlexibleMessageBoxFormBindingSource.DataSource = flexibleMessageBoxForm;
|
|
flexibleMessageBoxForm.titleLabel.Text = caption;
|
|
|
|
SetDialogButtons(flexibleMessageBoxForm, buttons, defaultButton);
|
|
SetDialogIcon(flexibleMessageBoxForm, icon);
|
|
|
|
flexibleMessageBoxForm.Font = FONT;
|
|
flexibleMessageBoxForm.richTextBoxMessage.Font = FONT;
|
|
|
|
SetDialogSizes(flexibleMessageBoxForm, text, caption);
|
|
|
|
// Force panel resize to fix closebutton position
|
|
int contentWidth = flexibleMessageBoxForm.ClientSize.Width - 16; // 8px padding
|
|
flexibleMessageBoxForm.titlePanel.Width = contentWidth;
|
|
|
|
SetDialogStartPosition(flexibleMessageBoxForm, owner);
|
|
|
|
return flexibleMessageBoxForm.ShowDialog(owner);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |