centro MessageBox en forma primaria

¿Hay una manera fácil de centrar MessageBox en forma primaria en .NET 2.0

De un comentario en el blog de Joel Spolsky :

Un Messagebox siempre se centra en la pantalla. Puede proporcionar un propietario, pero eso es solo por orden Z, no centrado. La única forma es usar ganchos Win32 y centrarlo usted mismo. Puede encontrar el código haciendo eso en línea si lo busca.

Mucho más fácil es simplemente escribir su propia clase de cuadro de mensaje y agregar funcionalidad de centrado. Luego, también puede agregar subtítulos predeterminados, No mostrar de nuevo, checkbox y hacerlos no modal.

“Win32 hooks” probablemente se refiere al uso de SetWindowsHookEx como se muestra en este ejemplo .

Realmente necesitaba esto en C # y encontré Center MessageBox C #

Aquí hay una versión muy formateada

 using System; using System.Windows.Forms; using System.Text; using System.Drawing; using System.Runtime.InteropServices; public class MessageBoxEx { private static IWin32Window _owner; private static HookProc _hookProc; private static IntPtr _hHook; public static DialogResult Show(string text) { Initialize(); return MessageBox.Show(text); } public static DialogResult Show(string text, string caption) { Initialize(); return MessageBox.Show(text, caption); } public static DialogResult Show(string text, string caption, MessageBoxButtons buttons) { Initialize(); return MessageBox.Show(text, caption, buttons); } public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) { Initialize(); return MessageBox.Show(text, caption, buttons, icon); } public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defButton) { Initialize(); return MessageBox.Show(text, caption, buttons, icon, defButton); } public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defButton, MessageBoxOptions options) { Initialize(); return MessageBox.Show(text, caption, buttons, icon, defButton, options); } public static DialogResult Show(IWin32Window owner, string text) { _owner = owner; Initialize(); return MessageBox.Show(owner, text); } public static DialogResult Show(IWin32Window owner, string text, string caption) { _owner = owner; Initialize(); return MessageBox.Show(owner, text, caption); } public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons) { _owner = owner; Initialize(); return MessageBox.Show(owner, text, caption, buttons); } public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) { _owner = owner; Initialize(); return MessageBox.Show(owner, text, caption, buttons, icon); } public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defButton) { _owner = owner; Initialize(); return MessageBox.Show(owner, text, caption, buttons, icon, defButton); } public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defButton, MessageBoxOptions options) { _owner = owner; Initialize(); return MessageBox.Show(owner, text, caption, buttons, icon, defButton, options); } public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam); public delegate void TimerProc(IntPtr hWnd, uint uMsg, UIntPtr nIDEvent, uint dwTime); public const int WH_CALLWNDPROCRET = 12; public enum CbtHookAction : int { HCBT_MOVESIZE = 0, HCBT_MINMAX = 1, HCBT_QS = 2, HCBT_CREATEWND = 3, HCBT_DESTROYWND = 4, HCBT_ACTIVATE = 5, HCBT_CLICKSKIPPED = 6, HCBT_KEYSKIPPED = 7, HCBT_SYSCOMMAND = 8, HCBT_SETFOCUS = 9 } [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, ref Rectangle lpRect); [DllImport("user32.dll")] private static extern int MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); [DllImport("User32.dll")] public static extern UIntPtr SetTimer(IntPtr hWnd, UIntPtr nIDEvent, uint uElapse, TimerProc lpTimerFunc); [DllImport("User32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); [DllImport("user32.dll")] public static extern int UnhookWindowsHookEx(IntPtr idHook); [DllImport("user32.dll")] public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int maxLength); [DllImport("user32.dll")] public static extern int EndDialog(IntPtr hDlg, IntPtr nResult); [StructLayout(LayoutKind.Sequential)] public struct CWPRETSTRUCT { public IntPtr lResult; public IntPtr lParam; public IntPtr wParam; public uint message; public IntPtr hwnd; } ; static MessageBoxEx() { _hookProc = new HookProc(MessageBoxHookProc); _hHook = IntPtr.Zero; } private static void Initialize() { if (_hHook != IntPtr.Zero) { throw new NotSupportedException("multiple calls are not supported"); } if (_owner != null) { _hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, _hookProc, IntPtr.Zero, AppDomain.GetCurrentThreadId()); } } private static IntPtr MessageBoxHookProc(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode < 0) { return CallNextHookEx(_hHook, nCode, wParam, lParam); } CWPRETSTRUCT msg = (CWPRETSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPRETSTRUCT)); IntPtr hook = _hHook; if (msg.message == (int)CbtHookAction.HCBT_ACTIVATE) { try { CenterWindow(msg.hwnd); } finally { UnhookWindowsHookEx(_hHook); _hHook = IntPtr.Zero; } } return CallNextHookEx(hook, nCode, wParam, lParam); } private static void CenterWindow(IntPtr hChildWnd) { Rectangle recChild = new Rectangle(0, 0, 0, 0); bool success = GetWindowRect(hChildWnd, ref recChild); int width = recChild.Width - recChild.X; int height = recChild.Height - recChild.Y; Rectangle recParent = new Rectangle(0, 0, 0, 0); success = GetWindowRect(_owner.Handle, ref recParent); Point ptCenter = new Point(0, 0); ptCenter.X = recParent.X + ((recParent.Width - recParent.X) / 2); ptCenter.Y = recParent.Y + ((recParent.Height - recParent.Y) / 2); Point ptStart = new Point(0, 0); ptStart.X = (ptCenter.X - (width / 2)); ptStart.Y = (ptCenter.Y - (height / 2)); ptStart.X = (ptStart.X < 0) ? 0 : ptStart.X; ptStart.Y = (ptStart.Y < 0) ? 0 : ptStart.Y; int result = MoveWindow(hChildWnd, ptStart.X, ptStart.Y, width, height, false); } } 

Pero, ¿por qué detenerse con la implementación específica de MessageBox? Usa la clase a continuación así:

  private void OnFormClosing(object sender, FormClosingEventArgs e) { DialogResult dg; using (DialogCenteringService centeringService = new DialogCenteringService(this)) // center message box { dg = MessageBox.Show(this, "Are you sure?", "Confirm exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question); } if (dg == DialogResult.No) { e.Cancel = true; } } 

El código que puede usar con cualquier elemento que muestre ventanas de diálogo, incluso si son propiedad de otro hilo (nuestra aplicación tiene varios hilos de interfaz de usuario):

( Aquí está el código actualizado que tiene en cuenta las áreas de trabajo del monitor, para que el diálogo no esté centrado entre dos monitores o esté parcialmente fuera de la pantalla. Con él necesitará enum SetWindowPosFlags , que se encuentra debajo)

 public class DialogCenteringService : IDisposable { private readonly IWin32Window owner; private readonly HookProc hookProc; private readonly IntPtr hHook = IntPtr.Zero; public DialogCenteringService(IWin32Window owner) { if (owner == null) throw new ArgumentNullException("owner"); this.owner = owner; hookProc = DialogHookProc; hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, hookProc, IntPtr.Zero, GetCurrentThreadId()); } private IntPtr DialogHookProc(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode < 0) { return CallNextHookEx(hHook, nCode, wParam, lParam); } CWPRETSTRUCT msg = (CWPRETSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPRETSTRUCT)); IntPtr hook = hHook; if (msg.message == (int)CbtHookAction.HCBT_ACTIVATE) { try { CenterWindow(msg.hwnd); } finally { UnhookWindowsHookEx(hHook); } } return CallNextHookEx(hook, nCode, wParam, lParam); } public void Dispose() { UnhookWindowsHookEx(hHook); } private void CenterWindow(IntPtr hChildWnd) { Rectangle recChild = new Rectangle(0, 0, 0, 0); bool success = GetWindowRect(hChildWnd, ref recChild); if (!success) { return; } int width = recChild.Width - recChild.X; int height = recChild.Height - recChild.Y; Rectangle recParent = new Rectangle(0, 0, 0, 0); success = GetWindowRect(owner.Handle, ref recParent); if (!success) { return; } Point ptCenter = new Point(0, 0); ptCenter.X = recParent.X + ((recParent.Width - recParent.X) / 2); ptCenter.Y = recParent.Y + ((recParent.Height - recParent.Y) / 2); Point ptStart = new Point(0, 0); ptStart.X = (ptCenter.X - (width / 2)); ptStart.Y = (ptCenter.Y - (height / 2)); //MoveWindow(hChildWnd, ptStart.X, ptStart.Y, width, height, false); Task.Factory.StartNew(() => SetWindowPos(hChildWnd, (IntPtr)0, ptStart.X, ptStart.Y, width, height, SetWindowPosFlags.SWP_ASYNCWINDOWPOS | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOZORDER)); } // some p/invoke // ReSharper disable InconsistentNaming public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam); public delegate void TimerProc(IntPtr hWnd, uint uMsg, UIntPtr nIDEvent, uint dwTime); private const int WH_CALLWNDPROCRET = 12; // ReSharper disable EnumUnderlyingTypeIsInt private enum CbtHookAction : int // ReSharper restre EnumUnderlyingTypeIsInt { // ReSharper disable UnusedMember.Local HCBT_MOVESIZE = 0, HCBT_MINMAX = 1, HCBT_QS = 2, HCBT_CREATEWND = 3, HCBT_DESTROYWND = 4, HCBT_ACTIVATE = 5, HCBT_CLICKSKIPPED = 6, HCBT_KEYSKIPPED = 7, HCBT_SYSCOMMAND = 8, HCBT_SETFOCUS = 9 // ReSharper restre UnusedMember.Local } [DllImport("kernel32.dll")] static extern int GetCurrentThreadId(); [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, ref Rectangle lpRect); [DllImport("user32.dll")] private static extern int MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags); [DllImport("User32.dll")] public static extern UIntPtr SetTimer(IntPtr hWnd, UIntPtr nIDEvent, uint uElapse, TimerProc lpTimerFunc); [DllImport("User32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); [DllImport("user32.dll")] public static extern int UnhookWindowsHookEx(IntPtr idHook); [DllImport("user32.dll")] public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int maxLength); [DllImport("user32.dll")] public static extern int EndDialog(IntPtr hDlg, IntPtr nResult); [StructLayout(LayoutKind.Sequential)] public struct CWPRETSTRUCT { public IntPtr lResult; public IntPtr lParam; public IntPtr wParam; public uint message; public IntPtr hwnd; }; // ReSharper restre InconsistentNaming } [Flags] public enum SetWindowPosFlags : uint { // ReSharper disable InconsistentNaming ///  /// If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request. ///  SWP_ASYNCWINDOWPOS = 0x4000, ///  /// Prevents generation of the WM_SYNCPAINT message. ///  SWP_DEFERERASE = 0x2000, ///  /// Draws a frame (defined in the window's class description) around the window. ///  SWP_DRAWFRAME = 0x0020, ///  /// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed. ///  SWP_FRAMECHANGED = 0x0020, ///  /// Hides the window. ///  SWP_HIDEWINDOW = 0x0080, ///  /// Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter). ///  SWP_NOACTIVATE = 0x0010, ///  /// Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned. ///  SWP_NOCOPYBITS = 0x0100, ///  /// Retains the current position (ignores X and Y parameters). ///  SWP_NOMOVE = 0x0002, ///  /// Does not change the owner window's position in the Z order. ///  SWP_NOOWNERZORDER = 0x0200, ///  /// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing. ///  SWP_NOREDRAW = 0x0008, ///  /// Same as the SWP_NOOWNERZORDER flag. ///  SWP_NOREPOSITION = 0x0200, ///  /// Prevents the window from receiving the WM_WINDOWPOSCHANGING message. ///  SWP_NOSENDCHANGING = 0x0400, ///  /// Retains the current size (ignores the cx and cy parameters). ///  SWP_NOSIZE = 0x0001, ///  /// Retains the current Z order (ignores the hWndInsertAfter parameter). ///  SWP_NOZORDER = 0x0004, ///  /// Displays the window. ///  SWP_SHOWWINDOW = 0x0040, // ReSharper restre InconsistentNaming } 

He cambiado un poco la respuesta anterior y redacto la versión WPF de MessageBoxEx. Este código funciona para mí genial. No dude en notificar acerca de los problemas del código.

Tenga en cuenta que utilizo GeneralObjects.MainWindowInstance at ctor para inicializar la clase con mi ventana principal, pero en realidad la utilizo para cualquier ventana debido a algún tipo de caché para la última ventana principal. Por lo tanto, puedes eliminar todo de ctor.

 public class MessageBoxEx { private static HwndSource source_ = null; private static HwndSourceHook hook_ = null; static MessageBoxEx() { try { // create cached createHwndSource_(GeneralObjects.MainWindowInstance); hook_ = new HwndSourceHook(HwndSourceHook); } finally { if (null == source_ || null == hook_) { source_ = null; hook_ = null; } } } private static void createHwndSource_(Window owner) { source_ = (HwndSource)PresentationSource.FromVisual(owner); } public static void Initialize_(Window owner = null) { try { if (null != owner) { if(source_.RootVisual != owner) { createHwndSource_(owner); } } } finally { if (null == source_ || null == hook_) { source_ = null; hook_ = null; } } if (null != source_ && null != hook_) { source_.AddHook(hook_); } } public static MessageBoxResult Show(string messageBoxText) { Initialize_(); return System.Windows.MessageBox.Show(messageBoxText); } public static MessageBoxResult Show(string messageBoxText, string caption) { Initialize_(); return System.Windows.MessageBox.Show(messageBoxText, caption); } public static MessageBoxResult Show(Window owner, string messageBoxText) { Initialize_(owner); return System.Windows.MessageBox.Show(owner, messageBoxText); } public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button) { Initialize_(); return System.Windows.MessageBox.Show(messageBoxText, caption, button); } public static MessageBoxResult Show(Window owner, string messageBoxText, string caption) { Initialize_(owner); return System.Windows.MessageBox.Show(owner, messageBoxText, caption); } public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon) { Initialize_(); return System.Windows.MessageBox.Show(messageBoxText, caption, button, icon); } public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button) { Initialize_(owner); return System.Windows.MessageBox.Show(owner, messageBoxText, caption, button); } public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult) { Initialize_(); return System.Windows.MessageBox.Show(messageBoxText, caption, button, icon, defaultResult); } public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon) { Initialize_(owner); return System.Windows.MessageBox.Show(owner, messageBoxText, caption, button, icon); } public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult, System.Windows.MessageBoxOptions options) { Initialize_(); return System.Windows.MessageBox.Show(messageBoxText, caption, button, icon, defaultResult, options); } public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult) { Initialize_(owner); return System.Windows.MessageBox.Show(owner, messageBoxText, caption, button, icon, defaultResult); } public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult, System.Windows.MessageBoxOptions options) { Initialize_(owner); return System.Windows.MessageBox.Show(owner, messageBoxText, caption, button, icon, defaultResult, options); } private enum WM : int { WM_ACTIVATE = 0x0006 } private static IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if ((int)WM.WM_ACTIVATE == msg && source_.Handle == hwnd && 0 == (int)wParam) { try { CenterWindow(lParam); } finally { // remove hook at once after moved message box window. source_.RemoveHook(hook_); } } return IntPtr.Zero; } [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, ref Rectangle lpRect); [DllImport("user32.dll")] private static extern int MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); private static void CenterWindow(IntPtr hChildWnd) { System.Drawing.Rectangle recChild = new System.Drawing.Rectangle(0, 0, 0, 0); bool success = GetWindowRect(hChildWnd, ref recChild); int width = recChild.Width - recChild.X; int height = recChild.Height - recChild.Y; System.Drawing.Rectangle recParent = new System.Drawing.Rectangle(0, 0, 0, 0); success = GetWindowRect(source_.Handle, ref recParent); System.Drawing.Point ptCenter = new System.Drawing.Point(0, 0); ptCenter.X = recParent.X + ((recParent.Width - recParent.X) / 2); ptCenter.Y = recParent.Y + ((recParent.Height - recParent.Y) / 2); System.Drawing.Point ptStart = new System.Drawing.Point(0, 0); ptStart.X = (ptCenter.X - (width / 2)); ptStart.Y = (ptCenter.Y - (height / 2)); // I have commented this code because of I have 2 monitors // so If application located at 1st monitor // message box can appear at second one. /* ptStart.X = (ptStart.X < 0) ? 0 : ptStart.X; ptStart.Y = (ptStart.Y < 0) ? 0 : ptStart.Y; */ int result = MoveWindow(hChildWnd, ptStart.X, ptStart.Y, width, height, false); } } 

Sin duda, utilizar su propio panel o formulario sería, con mucho, el enfoque más simple si un poco más pesado en el código de fondo (diseñador). Le da todo el control en términos de centrado y manipulación sin escribir todo ese código personalizado.

Prueba esto, es lo suficientemente simple como para justificar el tiempo …

Esto es para Win32 API, escrito en C. Traducirlo como lo necesite …

 case WM_NOTIFY:{ HWND X=FindWindow("#32770",NULL); if(GetParent(X)==H_frame){int Px,Py,Sx,Sy; RECT R1,R2; GetWindowRect(hwnd,&R1); GetWindowRect(X,&R2); Sx=R2.right-R2.left,Px=R1.left+(R1.right-R1.left)/2-Sx/2; Sy=R2.bottom-R2.top,Py=R1.top+(R1.bottom-R1.top)/2-Sy/2; MoveWindow(X,Px,Py,Sx,Sy,1); } } break; 

Añádalo al código de WndProc … Puede establecer la posición que desee, en este caso solo se centra en la ventana principal del progtwig. Hará esto para cualquier cuadro de mensaje, o abrir archivo / guardar diálogo, y probablemente algunos otros controles nativos. No estoy seguro, pero creo que puede necesitar incluir COMMCTRL o COMMDLG para usar esto, al menos, lo hará si desea abrir / guardar cuadros de diálogo.

Experimenté con mirar los códigos de notificación y hwndFrom de NMHDR, luego decidí que era igual de eficaz, y mucho más fácil, no hacerlo. Si realmente desea ser muy específico, indique a FindWindow que busque un título único (título) que le dé a la ventana que desea que encuentre.

Esto se dispara antes de dibujar el cuadro de mensaje en la pantalla, por lo que si establece un indicador global para indicar cuándo su código realiza la acción y busca un título único, asegúrese de que las acciones que realice solo se produzcan una vez (es probable que haya múltiples notificadores). No he explorado esto en detalle, pero logré obtener CreateWindow para poner un cuadro de edición en un diálogo de cuadro de mensaje. Parecía tan fuera de lugar como la oreja de una rata injertada en la espina dorsal de un cerdo clonado, pero funciona. Hacer las cosas de esta manera puede ser mucho más fácil que tener que hacer las suyas.

Cuervo.

EDITAR: Corrección pequeña para asegurarse de que se maneja la ventana correcta. Asegúrese de que los controladores de los padres estén de acuerdo en todo momento, y esto debería funcionar bien. Lo hace por mí, incluso con dos instancias del mismo progtwig …