【问题标题】:Winforms-How can I make MessageBox appear centered on MainForm?Winforms-如何让 MessageBox 以 MainForm 为中心显示?
【发布时间】:2011-02-04 07:00:03
【问题描述】:

Winforms-如何让对话框以 MainForm 为中心显示?这与基于将它们呈现在屏幕中心的普通窗口默认设置相反。

在我的例子中,我有一个小的主窗体,例如,它可能位于一个角落,MessageBox 弹出窗口会显示似乎很远的地方。

【问题讨论】:

  • using(NewFormDialog newDialog = new NewFormDialog()) { newDialog.StartPosition = FormStartPosition.CenterParent; newDialog.ShowDialog(); };
  • @uSeRnAmEhAhAhAhAhA whatsa NewFormDialog?

标签: winforms messagebox


【解决方案1】:

使用一些 P/Invoke 和 Control.BeginInvoke() 提供的魔法是可能的。向您的项目添加一个新类并粘贴此代码:

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; }
}

示例用法:

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

请注意,此代码适用于任何 Windows 对话框。 MessageBox、OpenFormDialog、FolderBrowserDialog、PrintDialog、ColorDialog、FontDialog、PageSetupDialog、SaveFileDialog。

【讨论】:

  • 谢谢,非常好的解决方案。您只需将其更改为不使用this,这是多余的。只需采取顶部形式。
  • 最好的+10000000000000
  • 这是一个疯狂的小工具。很好的解决方案。
  • 您需要对此进行一个小修复...如果主窗口被最小化,它会弄乱消息框。这可以通过在构造函数中将if (owner.WindowState != FormWindowState.Minimized) 放在owner.BeginInvoke 之前来避免,在这种情况下有效地禁用系统。
  • 我们向大师鞠躬! \/ 最伟大的先生,十二国感谢你。
【解决方案2】:

编写您自己的消息框。一个表格和一个标签应该做到这一点。还是你也需要全球化?

【讨论】:

  • 在这种情况下,您可以将标签添加到表单并使用 MeasureString 来获取它的边界。适当调整表格大小并将其放置在您喜欢的任何位置。应该很快。但我必须承认我也喜欢 Nobugz 的解决方案。
【解决方案3】:

这是用于 Win32 API,用 C 编写的。根据需要翻译它...

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;

将它添加到 WndProc 代码...您可以根据需要设置位置,在这种情况下,它只是位于主程序窗口的中心。它将对任何消息框或文件打开/保存对话框以及可能的其他一些本机控件执行此操作。我不确定,但我认为您可能需要包含 COMMCTRL 或 COMMDLG 才能使用它,至少,如果您想要打开/保存对话框,您会这样做。

我尝试查看 NMHDR 的通知代码和 hwndFrom,然后认为它同样有效,而且更容易,而不是。如果您真的想要非常具体,请告诉 FindWindow 查找您为希望它找到的窗口提供的唯一标题(标题)。

这会在消息框绘制到屏幕上之前触发,因此如果您设置一个全局标志来指示您的代码何时执行操作,并寻找一个唯一的标题,您可以确保您执行的操作只会发生一次(将会可能是多个通知者)。我没有对此进行详细探讨,但我设法让 CreateWindow 在消息框对话框上放置了一个编辑框/它看起来就像一只老鼠的耳朵移植到克隆猪的脊椎上一样不合适,但它确实有效。以这种方式做事可能比自己动手要容易得多。

乌鸦。

编辑:小修正以确保处理正确的窗口。确保父句柄始终一致,这应该可以正常工作。它对我有用,即使是同一个程序的两个实例...

【讨论】:

    【解决方案4】:

    该类被证明适用于其他两种情况。我有一个想要更大的 FolderBrowserDialog,我希望它出现在父对话框的左上角附近(靠近我单击以打开它的按钮)。

    我复制了 CenterWinDialog 类并创建了两个新类。一个类改变对话框大小,另一个改变它的位置到父窗体的特定偏移量。这是用法:

            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;
            }
    

    这是基于原始类的两个类。

    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; }
    }
    

    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; }
    }
    

    【讨论】:

      【解决方案5】:

      创建你自己的..

       public partial class __MessageBox : Form
         {
            public MMMessageBox(string title, string message)
            {
               InitializeComponent();
               this.Text = title;
               this.labelString.Text = message;
            }
         }
      

      【讨论】:

      • 您的帖子中没有任何内容处理“以 MainForm 为中心”的问题。
      • Sorry Lars,然后使用“表单属性”:StartPosition->CenterParent 作为您创建的对话框。
      • @gameOverMan 现在让它处理原始消息框具有的所有图标、是/否/确定/取消、默认选择按钮和DialogResult 响应案例。不完全是一项微不足道的任务。
      猜你喜欢
      • 2016-01-24
      • 1970-01-01
      • 2012-03-27
      • 1970-01-01
      • 2011-05-17
      • 2010-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多