using System; using System.Collections.Generic; using System.Linq; using System.Text; using Watchdog.WatchdogLib.Win32; using System.Windows.Forms; using System.Drawing; using System.ComponentModel; using WPFWin = System.Windows; using WPFWinInterop = System.Windows.Interop; namespace Watchdog.WatchdogLib.WinForms { /// /// MsgBox is a WindowsForms MessageBox that can be centered to the Parent /// public static class MsgBox { #region Private Static Members private static User32.WindowsHookProc _hookProcDelegate = null; private static int _hHook = 0; private static string _title = null; private static string _msg = null; private static IntPtr _hIcon = IntPtr.Zero; private static bool _IsDesktopOwner = false; private static bool _ShowFatal = false; // Delegate to make All Message Boxes Thread-Safe private delegate DialogResult ShowMsgBoxDelegate(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon, Icon TitleBarIcon); #endregion #region Public Properties public static Icon DefaultTitleBarIcon { get; set; } public static uint MaxNumberOfCharactersPerLine { get; set; } public const uint DefaultNumberOfCharactersPerLine = 62; #endregion #region Public Header Properties public static bool ShowHeader { get; set; } public static string MsgBox_ErrorHeader { get; set; } public static string MsgBox_FatalErrorHeader { get; set; } public static string MsgBox_WarningHeader { get; set; } public static string MsgBox_InfoHeader { get; set; } #endregion #region Public Footer Properties public static bool ShowFooter { get; set; } public static string MsgBox_ErrorFooter { get; set; } public static string MsgBox_FatalErrorFooter { get; set; } public static string MsgBox_WarningFooter { get; set; } public static string MsgBox_InfoFooter { get; set; } #endregion #region Public Title Header Properties public static bool ShowTitleHeader { get; set; } public static string MsgBox_ErrorTitleHeader { get; set; } public static string MsgBox_FatalErrorTitleHeader { get; set; } public static string MsgBox_WarningTitleHeader { get; set; } public static string MsgBox_InfoTitleHeader { get; set; } #endregion #region Construction static MsgBox() { DefaultTitleBarIcon = null; MaxNumberOfCharactersPerLine = DefaultNumberOfCharactersPerLine; // Header Init MsgBox_FatalErrorHeader = String.Empty; MsgBox_ErrorHeader = String.Empty; MsgBox_WarningHeader = String.Empty; MsgBox_InfoHeader = String.Empty; ShowHeader = true; // Footer Init MsgBox_FatalErrorFooter = String.Empty; MsgBox_ErrorFooter = String.Empty; MsgBox_WarningFooter = String.Empty; MsgBox_InfoFooter = String.Empty; ShowFooter = true; // Title Header Init MsgBox_FatalErrorTitleHeader = String.Empty; MsgBox_ErrorTitleHeader = String.Empty; MsgBox_WarningTitleHeader = String.Empty; MsgBox_InfoTitleHeader = String.Empty; ShowTitleHeader = true; } #endregion #region Public Static Methods /// /// Shows a custom Message Box (centered to parent), with the DefaultTitleBarIcon /// public static DialogResult Show(WPFWin.Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon) { if (parent != null) { WPFWinInterop.WindowInteropHelper interop = new WPFWinInterop.WindowInteropHelper(parent); if(interop.Handle != IntPtr.Zero) return Show(Watchdog.WatchdogLib.Win32.Convert.ConverthWndToIWin32Window(interop.Handle), Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); } return Show(Functions.GetDestopWindow(), Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); } /// /// Shows a custom Message Box (centered to parent), with the DefaultTitleBarIcon /// public static DialogResult Show(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon) { return Show(parent, Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); } /// /// Shows a custom Message Box (centered to Desktop), with the DefaultTitleBarIcon /// public static DialogResult Show(String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon) { return Show(Functions.GetDestopWindow(), Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); } /// /// *Main MessageBox Show Function* allows you to center the Message Box to the Parent /// /// Result of Dialog public static DialogResult Show(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon, Icon TitleBarIcon) { ISynchronizeInvoke InvokeObject = null; if (parent != null && parent is ISynchronizeInvoke) InvokeObject = (ISynchronizeInvoke)parent; // Invoke if we need to * Make MessageBoxes generally Thread-safe * if ((InvokeObject != null) && InvokeObject.InvokeRequired) { DialogResult result = (DialogResult)InvokeObject.Invoke(new ShowMsgBoxDelegate(MsgBox.Show), new object[] { parent, Title, Text, MsgBoxButtons, MsgBoxIcon, TitleBarIcon }); return result; } else { return MsgBox.ShowInternal(parent, Text, Title, MsgBoxButtons, MsgBoxIcon, TitleBarIcon); } } #endregion #region Public Static Methods Extended /// /// Shows Warning MessageBox /// //public static DialogResult ShowWarning(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowWarning(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) { return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Exclamation); } /// /// Shows Warning MessageBox /// //public static DialogResult ShowWarning(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowWarning(String Text, String Title, MessageBoxButtons MsgBoxButtons) { return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Exclamation); } /// /// Shows Fatal Error MessageBox /// //public static DialogResult ShowFatalError(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowFatalError(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) { _ShowFatal = true; DialogResult dr = Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Error); _ShowFatal = false; return dr; } /// /// Shows Fatal Error MessageBox /// //public static DialogResult ShowFatalError(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowFatalError(String Text, String Title, MessageBoxButtons MsgBoxButtons) { _ShowFatal = true; DialogResult dr = Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Error); _ShowFatal = false; return dr; } /// /// Shows Error MessageBox /// //public static DialogResult ShowError(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowError(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) { return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Error); } /// /// Shows Error MessageBox /// //public static DialogResult ShowError(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowError(String Text, String Title, MessageBoxButtons MsgBoxButtons) { return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Error); } /// /// Shows Information MessageBox /// //public static DialogResult ShowInfo(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowInfo(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) { return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Information); } /// /// Shows Information MessageBox /// //public static DialogResult ShowInfo(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowInfo(String Text, String Title, MessageBoxButtons MsgBoxButtons) { return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Information); } /// /// Shows Default MessageBox /// //public static DialogResult ShowDefault(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowDefault(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) { return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.None); } /// /// Shows Default MessageBox /// //public static DialogResult ShowDefault(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) public static DialogResult ShowDefault(String Text, String Title, MessageBoxButtons MsgBoxButtons) { return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.None); } #endregion #region Private Methods /// /// /// /// /// /// /// /// /// /// private static DialogResult ShowInternal(IWin32Window owner, string msg, string title, MessageBoxButtons btns, MessageBoxIcon icon, Icon TitleBarIcon) { // Create a callback delegate _hookProcDelegate = new User32.WindowsHookProc(HookCallback); #region Header Action if (ShowHeader) { if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_FatalErrorHeader) && _ShowFatal) msg = MsgBox_FatalErrorHeader + msg; else if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_ErrorHeader) && !_ShowFatal) msg = MsgBox_ErrorHeader + msg; else if (icon == MessageBoxIcon.Exclamation && !String.IsNullOrEmpty(MsgBox_WarningHeader)) msg = MsgBox_WarningHeader + msg; else if (icon == MessageBoxIcon.Information && !String.IsNullOrEmpty(MsgBox_InfoHeader)) msg = MsgBox_InfoHeader + msg; } #endregion #region Footer Action if (ShowFooter) { if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_FatalErrorFooter) && _ShowFatal) msg = msg + MsgBox_FatalErrorFooter; else if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_ErrorFooter) && !_ShowFatal) msg = msg + MsgBox_ErrorFooter; else if (icon == MessageBoxIcon.Exclamation && !String.IsNullOrEmpty(MsgBox_WarningFooter)) msg = msg + MsgBox_WarningFooter; else if (icon == MessageBoxIcon.Information && !String.IsNullOrEmpty(MsgBox_InfoFooter)) msg = msg + MsgBox_InfoFooter; } #endregion #region Title Header if (ShowTitleHeader) { if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_FatalErrorTitleHeader) && _ShowFatal) title = MsgBox_FatalErrorTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); else if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_ErrorTitleHeader) && !_ShowFatal) title = MsgBox_ErrorTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); else if (icon == MessageBoxIcon.Exclamation && !String.IsNullOrEmpty(MsgBox_WarningTitleHeader)) title = MsgBox_WarningTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); else if (icon == MessageBoxIcon.Information && !String.IsNullOrEmpty(MsgBox_InfoTitleHeader)) title = MsgBox_InfoTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); } #endregion #region Text Padding // Stripe last \n, if exists if (!String.IsNullOrEmpty(msg) && (msg.Length > 0) && (msg[msg.Length - 1] == '\n')) msg = msg.Remove(msg.Length - 1); // Make sure the text looks good, by using padding if (!String.IsNullOrEmpty(msg) && (msg.Length > 0) && (msg.Length < MaxNumberOfCharactersPerLine)) { string[] lines = msg.Split('\n'); StringBuilder sb = new StringBuilder(); foreach (string line in lines) { sb.Append(line.PadRight((int)MaxNumberOfCharactersPerLine)); sb.Append("\n"); } msg = sb.ToString(); } else if (!String.IsNullOrEmpty(msg) && (msg.Length > 0) && (msg.Length > MaxNumberOfCharactersPerLine)) { // Incredible and amazing Padding code string[] lines = msg.Split('\n'); StringBuilder sb = new StringBuilder(); for (int i = 0; i < lines.Length; ++i) { if (lines[i].Length < MaxNumberOfCharactersPerLine) { sb.Append(lines[i].PadRight((int)MaxNumberOfCharactersPerLine)); sb.Append("\n"); } else if (lines[i].Length > MaxNumberOfCharactersPerLine) { // truncate for (int j = 0; j < lines[i].Length; j = j + (int)MaxNumberOfCharactersPerLine) { string strSub = lines[i].Substring(j, ((lines[i].Length - j) > (int)MaxNumberOfCharactersPerLine) ? (int)MaxNumberOfCharactersPerLine : (lines[i].Length - j)); if (strSub.Length == (int)MaxNumberOfCharactersPerLine) { sb.Append(strSub); sb.Append("\n"); } else { sb.Append(strSub.PadRight((int)MaxNumberOfCharactersPerLine)); sb.Append("\n"); } } } else { sb.Append(lines[i]); sb.Append("\n"); } } // Write nicely formatted Message out msg = sb.ToString(); } else { // do nothing, string is miracioulsy exactly correct } #endregion // Remember the title & message that we'll look for. // The hook sees *all* windows, so we need to make sure we operate on the right one. _msg = msg; _title = title; // if Owner is the Desktop Window _IsDesktopOwner = (owner.Handle == Functions.GetDestopWindow().Handle); // Icon could not be present if (TitleBarIcon != null) _hIcon = TitleBarIcon.ToBitmap().GetHicon(); // Set the hook. // Suppress "GetCurrentThreadId() is deprecated" warning. // It's documented that Thread.ManagedThreadId doesn't work with SetWindowsHookEx() #pragma warning disable 0618 _hHook = User32.SetWindowsHookEx(Definitions.WH_CBT, _hookProcDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId()); #pragma warning restore 0618 // Pop a standard MessageBox. The hook will center it. DialogResult rslt = DialogResult.None; if (_IsDesktopOwner) rslt = MessageBox.Show(_msg, _title, btns, icon); else rslt = MessageBox.Show(owner, _msg, _title, btns, icon); // Release hook, clean up (may have already occurred) Unhook(); return rslt; } private static void Unhook() { User32.UnhookWindowsHookEx(_hHook); _hHook = 0; _hookProcDelegate = null; _msg = null; _title = null; } private static int HookCallback(int code, IntPtr wParam, IntPtr lParam) { int hHook = _hHook; // Local copy for CallNextHookEx() JIC we release _hHook // Look for HCBT_ACTIVATE, *not* HCBT_CREATEWND: // child controls haven't yet been created upon HCBT_CREATEWND. if (code == Definitions.HCBT_ACTIVATE) { string cls = Functions.GetWindowClassName(wParam); if (cls == "#32770") // MessageBoxes are Dialog boxes { string title = Functions.GetWindowText(wParam); string msg = Functions.GetDlgItemText(wParam, 0xFFFF); // -1 aka IDC_STATIC if ((title == _title) && (msg == _msg)) { // Only Center the Window, if the Owner is NOT the Desktop if (!_IsDesktopOwner) CenterWindowOnParent(wParam); Unhook(); // Release hook - we've done what we needed // Now we also want to set the Icon on the Dialog if (_hIcon != IntPtr.Zero) { User32.SendMessage(wParam, (int)Definitions.WM.WM_SETICON, (IntPtr)1, _hIcon); User32.SendMessage(wParam, (int)Definitions.WM.WM_SETICON, (IntPtr)0, _hIcon); } } } } return User32.CallNextHookEx(hHook, code, wParam, lParam); } // Boilerplate window-centering code. // Split out of HookCallback() for clarity. private static void CenterWindowOnParent(IntPtr hChildWnd) { // Get child (MessageBox) size Structures.RECT rcChild = new Structures.RECT(); User32.GetWindowRect(hChildWnd, out rcChild); int cxChild = rcChild.right - rcChild.left; int cyChild = rcChild.bottom - rcChild.top; // Get parent (Form) size & location IntPtr hParent = User32.GetParent(hChildWnd); Structures.RECT rcParent = new Structures.RECT(); User32.GetWindowRect(hParent, out rcParent); int cxParent = rcParent.right - rcParent.left; int cyParent = rcParent.bottom - rcParent.top; // Center the MessageBox on the Form int x = rcParent.left + (cxParent - cxChild) / 2; int y = rcParent.top + (cyParent - cyChild) / 2; uint uFlags = 0x15; // SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE; User32.SetWindowPos(hChildWnd, IntPtr.Zero, x, y, 0, 0, uFlags); } #endregion } }