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

7eumitmz  于 2023-06-06  发布在  其他
关注(0)|答案(6)|浏览(196)

Winforms-如何使对话框显示在MainForm的中心?这与基于正常窗口的默认设置相反,该默认设置将它们呈现在屏幕的中心。
在我的情况下,我有一个小的主窗体,例如,可能被定位在一个角落里,消息框弹出显示什么似乎是一种方式了。

pcrecxhr

pcrecxhr1#

使用一些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。

kx5bkwkv

kx5bkwkv2#

这是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在消息框对话框上放了一个编辑框/它看起来就像把老鼠的耳朵移植到克隆Pig的脊柱上一样不合时宜,但它确实有效。这样做可能比你自己做要容易得多。
乌鸦
编辑:小的修正,以确保正确的窗口处理。确保父句柄始终一致,这样应该可以正常工作。对我来说,即使是同一个程序的两个示例...

l5tcr1uw

l5tcr1uw3#

这个类被证明适用于另外两种情况。我有一个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; }
}
nfeuvbwi

nfeuvbwi4#

写你自己的消息框。一个表单和一个标签就可以了。或者你也需要全球化吗?

l2osamch

l2osamch5#

无需自制messagebox或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的 Package 。根据需要添加其他Pinvoke方法。如果你想尽可能地减少P/Invoke,用UIAutomation替换它们。

private string _messageBoxCaption = null; // messageBox caption

_messageBoxCaption = caption;
ret = MessageBox.Show(this, text, caption, ...);
_messageBoxCaption = null;
7fhtutme

7fhtutme6#

创建您自己的..

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

相关问题