2010-04-04 13 views
37

Winforms: ¿cómo puedo hacer que los cuadros de diálogo aparezcan centrados en MainForm? Eso es opuesto a estar basado en el valor predeterminado de Windows Normal que los representa en el centro de la pantalla.Winforms: ¿cómo puedo hacer que MessageBox aparezca centrado en MainForm?

En mi caso, tengo un pequeño formulario principal que puede, por ejemplo, colocarse en una esquina, en la ventana emergente MessageBox se muestra lo que parece estar lejos.

+1

'usando (NewFormDialog NewDialog = new NewFormDialog()) {newDialog.StartPosition = FormStartPosition.CenterParent; newDialog.ShowDialog(); }; ' – uSeRnAmEhAhAhAhAhA

Respuesta

48

Es posible con algunas porciones de P/Invoke y la magia proporcionada por Control.BeginInvoke(). Añadir una nueva clase a su proyecto y pega este código: el uso

using System; 
using System.Text; 
using System.Drawing; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

class CenterWinDialog : IDisposable { 
    private int mTries = 0; 
    private Form mOwner; 

    public CenterWinDialog(Form owner) { 
     mOwner = owner; 
     owner.BeginInvoke(new MethodInvoker(findDialog)); 
    } 

    private void findDialog() { 
     // Enumerate windows to find the message box 
     if (mTries < 0) return; 
     EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow); 
     if (EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero)) { 
      if (++mTries < 10) mOwner.BeginInvoke(new MethodInvoker(findDialog)); 
     } 
    } 
    private bool checkWindow(IntPtr hWnd, IntPtr lp) { 
     // Checks if <hWnd> is a dialog 
     StringBuilder sb = new StringBuilder(260); 
     GetClassName(hWnd, sb, sb.Capacity); 
     if (sb.ToString() != "#32770") return true; 
     // Got it 
     Rectangle frmRect = new Rectangle(mOwner.Location, mOwner.Size); 
     RECT dlgRect; 
     GetWindowRect(hWnd, out dlgRect); 
     MoveWindow(hWnd, 
      frmRect.Left + (frmRect.Width - dlgRect.Right + dlgRect.Left)/2, 
      frmRect.Top + (frmRect.Height - dlgRect.Bottom + dlgRect.Top)/2, 
      dlgRect.Right - dlgRect.Left, 
      dlgRect.Bottom - dlgRect.Top, true); 
     return false; 
    } 
    public void Dispose() { 
     mTries = -1; 
    } 

    // P/Invoke declarations 
    private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp); 
    [DllImport("user32.dll")] 
    private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp); 
    [DllImport("kernel32.dll")] 
    private static extern int GetCurrentThreadId(); 
    [DllImport("user32.dll")] 
    private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen); 
    [DllImport("user32.dll")] 
    private static extern bool GetWindowRect(IntPtr hWnd, out RECT rc); 
    [DllImport("user32.dll")] 
    private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint); 
    private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } 
} 

muestra:

private void button1_Click(object sender, EventArgs e) { 
     using (new CenterWinDialog(this)) { 
      MessageBox.Show("Nobugz waz here"); 
     } 
    } 

Tenga en cuenta que este código funciona para cualquiera de los cuadros de diálogo de Windows. MessageBox, OpenFormDialog, FolderBrowserDialog, PrintDialog, ColorDialog, FontDialog, PageSetupDialog, SaveFileDialog.

+0

Gracias, muy buena solución. Solo necesita cambiarlo para no usar 'this', que es redundante. Simplemente toma la forma superior. – c00000fd

+0

He adaptado este código a WPF y está funcionando: D – monstr

+0

El mejor +10000000000000 – vietnguyen09

1

Escriba su propio cuadro de mensaje. Una forma y una etiqueta deberían hacerlo. ¿O también necesitas globalizarlo?

+0

No necesita globalizar – Greg

+0

En ese caso, puede agregar una etiqueta a un formulario y usar MeasureString para obtener los límites del mismo. Calibre la forma de forma adecuada y colóquela donde desee. Debería ser rápido. Pero debo admitir que también me gusta la solución de Nobugz. – Pedery

2

Esto es para Win32 API, escrito en C traducen como usted necesita ...

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ñadir que al código WndProc ... Puede establecer la posición que desee, en este caso solo se centra en la ventana principal del programa. 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 que el cuadro de mensaje aparezca en la pantalla, por lo que si configura una bandera global para indicar cuándo realiza su acción y busca un título único, asegúrese de que las acciones que realice solo se produzcan una vez (probablemente serán varios notificadores). No he explorado esto en detalle, pero logré que CreateWindow pusiera un cuadro de edición en un cuadro de diálogo de mensaje/Parecía tan fuera de lugar como una oreja de 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 programa ...

1

La clase demostró ser aplicable a otras dos situaciones. Tenía un FolderBrowserDialog que quería que fuera más grande, y quería que apareciera cerca de la esquina superior izquierda del cuadro de diálogo principal (cerca del botón en el que hago clic para abrirlo).

Copié la clase CenterWinDialog e hice dos nuevas clases. Una clase cambia el tamaño del diálogo, y la otra cambia su posición a un desplazamiento específico de la forma principal. Este es el uso:

 using (new OffsetWinDialog(this) { PreferredOffset = new Point(75, 75)}) 
     using (new SizeWinDialog(this) { PreferredSize = new Size(400, 600)}) 
     { 
      DialogResult result = dlgFolderBrowser.ShowDialog(); 
      if (result == DialogResult.Cancel) 
       return; 
     } 

y estas son las dos clases que se basaron en la original.

class OffsetWinDialog : IDisposable 
{ 
    private int mTries = 0; 
    private Form mOwner; 

    public OffsetWinDialog(Form owner) 
    { 
     mOwner = owner; 
     owner.BeginInvoke(new MethodInvoker(findDialog)); 
    } 

    public Point PreferredOffset { get; set; } 

    private void findDialog() 
    { 
     // Enumerate windows to find the message box 
     if (mTries < 0) 
      return; 
     EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow); 
     if (EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero)) 
     { 
      if (++mTries < 10) 
       mOwner.BeginInvoke(new MethodInvoker(findDialog)); 
     } 
    } 
    private bool checkWindow(IntPtr hWnd, IntPtr lp) 
    { 
     // Checks if <hWnd> is a dialog 
     StringBuilder sb = new StringBuilder(260); 
     GetClassName(hWnd, sb, sb.Capacity); 
     if (sb.ToString() != "#32770") return true; 
     // Got it 
     Rectangle frmRect = new Rectangle(mOwner.Location, mOwner.Size); 
     RECT dlgRect; 
     GetWindowRect(hWnd, out dlgRect); 
     MoveWindow(hWnd, 
      frmRect.Left + PreferredOffset.X, 
      frmRect.Top + PreferredOffset.Y, 
      dlgRect.Right - dlgRect.Left, 
      dlgRect.Bottom - dlgRect.Top, 
      true); 
     return false; 
    } 
    public void Dispose() 
    { 
     mTries = -1; 
    } 

    // P/Invoke declarations 
    private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp); 
    [DllImport("user32.dll")] 
    private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp); 
    [DllImport("kernel32.dll")] 
    private static extern int GetCurrentThreadId(); 
    [DllImport("user32.dll")] 
    private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen); 
    [DllImport("user32.dll")] 
    private static extern bool GetWindowRect(IntPtr hWnd, out RECT rc); 
    [DllImport("user32.dll")] 
    private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint); 
    private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } 
} 

y

class SizeWinDialog : IDisposable 
{ 
    private int mTries = 0; 
    private Form mOwner; 

    public SizeWinDialog(Form owner) 
    { 
     mOwner = owner; 
     mOwner.BeginInvoke(new Action(findDialog)); 
    } 

    public Size PreferredSize { get; set; } 

    private void findDialog() 
    { 
     // Enumerate windows to find the message box 
     if (mTries < 0) 
      return; 
     EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow); 
     if (EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero)) 
     { 
      if (++mTries < 10) 
       mOwner.BeginInvoke(new MethodInvoker(findDialog)); 
     } 
    } 
    private bool checkWindow(IntPtr hWnd, IntPtr lp) 
    { 
     // Checks if <hWnd> is a dialog 
     StringBuilder sb = new StringBuilder(260); 
     GetClassName(hWnd, sb, sb.Capacity); 
     if (sb.ToString() != "#32770") 
      return true; 
     // Got it 
     Rectangle frmRect = new Rectangle(mOwner.Location, mOwner.Size); 
     RECT dlgRect; 
     GetWindowRect(hWnd, out dlgRect); 
     SetWindowPos(new HandleRef(this, hWnd), new HandleRef(), dlgRect.Left, dlgRect.Top, PreferredSize.Width, PreferredSize.Height, 20 | 2); 
     return false; 
    } 
    public void Dispose() 
    { 
     mTries = -1; 
    } 

    // P/Invoke declarations 
    private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp); 
    [DllImport("user32.dll")] 
    private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp); 
    [DllImport("kernel32.dll")] 
    private static extern int GetCurrentThreadId(); 
    [DllImport("user32.dll")] 
    private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen); 
    [DllImport("user32.dll")] 
    private static extern bool GetWindowRect(IntPtr hWnd, out RECT rc); 
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] 
    public static extern bool SetWindowPos(HandleRef hWnd, HandleRef hWndInsertAfter, int x, int y, int cx, int cy, 
     int flags); 

    private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } 
} 
Cuestiones relacionadas