Let me give you the background.
We have an Application(medium sized) that is using MessageBox.Show (....) at various places (in hundreds).
These message bo
This topic has been abundantly covered in other SO questions but since this particular one has several answers about using UI automation/window lookup techniques (which I don't particularly like) and generic suggestions about creating own dialog without provided code, I decided post my own solution. One can create an instantiable MessageBox
like class as it follows:
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace Common
{
// Loosely based on: https://www.codeproject.com/Articles/17253/A-Custom-Message-Box
class MsgBox : Form
{
private Panel _plHeader = new Panel();
private Panel _plFooter = new Panel();
private Panel _plIcon = new Panel();
private PictureBox _picIcon = new PictureBox();
private FlowLayoutPanel _flpButtons = new FlowLayoutPanel();
private Label _lblMessage;
private MsgBox()
{
FormBorderStyle = FormBorderStyle.FixedDialog;
BackColor = Color.White;
StartPosition = FormStartPosition.CenterScreen;
MinimizeBox = false;
MaximizeBox = false;
ShowIcon = false;
Width = 400;
_lblMessage = new Label();
_lblMessage.Font = new Font("Segoe UI", 10);
_lblMessage.Dock = DockStyle.Fill;
_lblMessage.TextAlign = ContentAlignment.MiddleLeft;
_flpButtons.FlowDirection = FlowDirection.RightToLeft;
_flpButtons.Dock = DockStyle.Fill;
//_plHeader.FlowDirection = FlowDirection.TopDown;
_plHeader.Dock = DockStyle.Fill;
_plHeader.Padding = new Padding(20);
_plHeader.Controls.Add(_lblMessage);
_plFooter.Dock = DockStyle.Bottom;
_plFooter.BackColor = Color.FromArgb(240, 240, 240);
_plFooter.Padding = new Padding(10);
_plFooter.Height = 60;
_plFooter.Controls.Add(_flpButtons);
_picIcon.Location = new Point(30, 50);
_plIcon.Dock = DockStyle.Left;
_plIcon.Padding = new Padding(20);
_plIcon.Width = 70;
_plIcon.Controls.Add(_picIcon);
Controls.Add(_plHeader);
Controls.Add(_plIcon);
Controls.Add(_plFooter);
}
public static DialogResult Show(IWin32Window owner, string message, string title = null, MessageBoxButtons? buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Information)
{
var msgBox = Create(message, title, buttons, icon);
return msgBox.ShowDialog(owner);
}
public static DialogResult Show(string message, string title = null, MessageBoxButtons? buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Information)
{
var msgBox = Create(message, title, buttons, icon);
return msgBox.ShowDialog();
}
public static MsgBox Create(string message, string title = null, MessageBoxButtons? buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.Information)
{
var msgBox = new MsgBox();
msgBox.Init(message, title, buttons, icon);
return msgBox;
}
void Init(string message, string title, MessageBoxButtons? buttons, MessageBoxIcon icon)
{
_lblMessage.Text = message;
Text = title;
InitButtons(buttons);
InitIcon(icon);
Size = MessageSize(message);
}
void InitButtons(MessageBoxButtons? buttons)
{
if (!buttons.HasValue)
return;
switch (buttons)
{
case MessageBoxButtons.AbortRetryIgnore:
AddButton("Ignore");
AddButton("Retry");
AddButton("Abort");
break;
case MessageBoxButtons.OK:
AddButton("OK");
break;
case MessageBoxButtons.OKCancel:
AddButton("Cancel");
AddButton("OK");
break;
case MessageBoxButtons.RetryCancel:
AddButton("Cancel");
AddButton("Retry");
break;
case MessageBoxButtons.YesNo:
AddButton("No");
AddButton("Yes");
break;
case MessageBoxButtons.YesNoCancel:
AddButton("Cancel");
AddButton("No");
AddButton("Yes");
break;
}
}
void InitIcon(MessageBoxIcon icon)
{
switch (icon)
{
case MessageBoxIcon.None:
_picIcon.Hide();
break;
case MessageBoxIcon.Exclamation:
_picIcon.Image = SystemIcons.Exclamation.ToBitmap();
break;
case MessageBoxIcon.Error:
_picIcon.Image = SystemIcons.Error.ToBitmap();
break;
case MessageBoxIcon.Information:
_picIcon.Image = SystemIcons.Information.ToBitmap();
break;
case MessageBoxIcon.Question:
_picIcon.Image = SystemIcons.Question.ToBitmap();
break;
}
_picIcon.Width = _picIcon.Image.Width;
_picIcon.Height = _picIcon.Image.Height;
}
private void ButtonClick(object sender, EventArgs e)
{
Button btn = (Button)sender;
switch (btn.Text)
{
case "Abort":
DialogResult = DialogResult.Abort;
break;
case "Retry":
DialogResult = DialogResult.Retry;
break;
case "Ignore":
DialogResult = DialogResult.Ignore;
break;
case "OK":
DialogResult = DialogResult.OK;
break;
case "Cancel":
DialogResult = DialogResult.Cancel;
break;
case "Yes":
DialogResult = DialogResult.Yes;
break;
case "No":
DialogResult = DialogResult.No;
break;
}
Close();
}
private static Size MessageSize(string message)
{
int width=350;
int height = 230;
SizeF size = TextRenderer.MeasureText(message, new Font("Segoe UI", 10));
if (message.Length < 150)
{
if ((int)size.Width > 350)
{
width = (int)size.Width;
}
}
else
{
string[] groups = (from Match m in Regex.Matches(message, ".{1,180}") select m.Value).ToArray();
int lines = groups.Length+1;
width = 700;
height += (int)(size.Height+10) * lines;
}
return new Size(width, height);
}
private void AddButton(string caption)
{
var btn = new Button();
btn.Text = caption;
btn.Font = new Font("Segoe UI", 8);
btn.BackColor = Color.FromArgb(225, 225, 225);
btn.Padding = new Padding(3);
btn.Height = 30;
btn.Click += ButtonClick;
_flpButtons.Controls.Add(btn);
}
}
}
One can then just keep the reference of the dialog in a class scope, show the dialog and get the result, or just close it in an application exit event handler.
MsgBox _msgBox;
void eventHandler1(object sender, EventArgs e)
{
_msgBox = MsgBox.Create("Do you want to continue", "Inquiry", MessageBoxButtons.YesNo);
var result = _msgBox.ShowDialog();
// do something with result
}
void applicationExitHandler(object sender, EventArgs e)
{
if (_msgBox != null)
_msgBox.Close();
}
Create your own control for this and implement behavior you like to have there. As an option there may be a timer to close this MessageBox.
The easiest solution is to create a form that will close on timer_tick
private int interval = 0;
private string message = "";
public msgBox(string msg = "", int i = 0)
{
InitializeComponent();
interval = i;
message = msg;
}
private void MsgBox_Load(object sender, EventArgs e)
{
if (interval > 0)
timer1.Interval = interval;
lblMessage.Text = message;
lblMessage.Width = panel1.Width - 20;
lblMessage.Left = 10;
}
private void Timer1_Tick(object sender, EventArgs e)
{
this.Close();
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
ControlPaint.DrawBorder(e.Graphics, this.panel1.ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Solid);
}
Method to use in main form
private void showMessage(string msg, int interval = 0)
{
msgBox mb = new msgBox(msg, interval);
mb.ShowDialog(this);
}
Call it
showMessage("File saved");
First a Question: If messages boxes are used as part of workflow, won't programatically closing message box cause the flow to change/continue?
I think you have three options
Create your own version of the messagebox class that opens a dialog window that looks like a messagebox with added functionality so it closed automatically after a period of time.
Implement something like this in c# to close message boxes programtically. http://www.codeproject.com/KB/dialog/AutoCloseMessageBox.aspx
Get rid of the message boxes from interupting the workflow. This is probably the best solution as from the sound of it closing a message box programatically will cause workflow to continue/change, and perhaps even cause another messagebox to show which may not be desirable. But obviously fixing the root problem might be best, but isn't always the easiest.
1 and 2 would need to be done from a separate thread, so you will need to think about the implications of that as showing the messagebox will be blocking.
Taking as an assumption that you can edit the code that's calling the
MessageBox.Show()
method, I would recommend not use
MessageBox. Instead, just use your own custom form, calling ShowDialog()
on it to do basically the same thing as the MessageBox class. Then, you
have the instance of the form itself, and you can call Close()
on that
instance to close it.
A good example is here.