8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

Winforms-如何使 MessageBox 显示在 MainForm 的中心?

Gus D 2月前

59 0

Winforms-如何让对话框显示在 MainForm 的中心?这与基于普通窗口默认设置相反,后者将它们显示在屏幕的中心。在我的例子中,我有一个小的

Winforms-如何使对话框显示在 MainForm 的中心?这与基于普通窗口默认设置(将对话框显示在屏幕中心)相反。

在我的例子中,我有一个小的主窗体,例如,它可能被放置在一个角落里,MessageBox 弹出窗口显示在看似很远的地方。

帖子版权声明 1、本帖标题:Winforms-如何使 MessageBox 显示在 MainForm 的中心?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Gus D在本站《winforms》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 借助 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。

  • 谢谢,非常好的解决方案。你只需要更改它以不使用这个多余的。只需采用顶部表格即可。

  • 您需要对此进行一个小修复...如果主窗口最小化,它会弄乱消息框。可以通过在构造函数中将 if (owner.WindowState != FormWindowState.Minimized) 放在owner.BeginInvoke 之前来避免这种情况,在这种情况下有效地禁用系统。

  • 这是用 C 语言编写的 Win32 API。请根据需要进行翻译...

    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 在消息框对话框中放置一个编辑框/它看起来像老鼠的耳朵嫁接到克隆猪的脊椎上一样不合适,但它确实有效。以这种方式做事可能比自己动手容易得多。

    乌鸦。

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

  • 事实证明,该类还适用于其他两种情况。我有一个 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; }
    }
    
  • 不需要自制消息框或 GetForegroundWindow、EnumWindows、AutomationElement.RootElement.FindAll、SetWindowsHookEx 等。

    当消息框打开或者关闭的时候都会向窗体发送WM_ACTIVATE消息,然后就可以得到消息框的窗口句柄(LParam)。

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg) {
        case Pinvoke.WM_ACTIVATE:
            Debug.WriteLine($"{MethodBase.GetCurrentMethod().Name} {DateTime.Now.ToString("HH:mm:ss.fff")} {m.ToString()}");
            if (m.LParam == IntPtr.Zero) break;
            if (_messageBoxCaption == null) break; // donot call MessageBox.Show
            if ((ushort)m.WParam.ToInt32() != 0/*WA_INACTIVE*/) break; // maybe close messagebox
    
            // check messagebox
            if (Pinvoke.GetWindowProcessId(m.LParam) != Process.GetCurrentProcess().Id) break;
            string className = Pinvoke.GetClassName(m.LParam);
            if (className == null || className != "#32770") break; // not dialog
            if (_messageBoxCaption != Pinvoke.GetWindowText(m.LParam)) break; // another caption
    
            // move messagebox
            //Debug.WriteLine("messageBox detected");
            Rectangle rect = Pinvoke.GetWindowRect(m.LParam);
            Pinvoke.MoveWindow(m.LParam, this.Left + this.Width / 2, this.Top + this.Height / 2, rect.Width, rect.Height, true);
            break;
        }
    
        base.WndProc(ref m);
    }
    

    GetWindowProcessId 是 GetWindowThreadProcessId 的包装器。根据需要添加其他 Pinvoke 方法。如果要尽可能减少 P/Invoke,请将其替换为 UIAutomation。

    private string _messageBoxCaption = null; // messageBox caption
    
    _messageBoxCaption = caption;
    ret = MessageBox.Show(this, text, caption, ...);
    _messageBoxCaption = null;
    
  • 在这种情况下,您可以向表单添加标签并使用 MeasureString 获取其边界。适当调整表单大小并将其放置在您喜欢的任何位置。应该很快。但我必须承认我也喜欢 Nobugz 的解决方案。

  • 这实际上忽略了重现消息框可能具有的多种变化所涉及的复杂性,使新 API 成为原始 API 的“替代品”,修改所有现有代码以使用替代方案等。此外,老实说,它并没有真正回答具体的问题。

返回
作者最近主题: