web-dev-qa-db-ja.com

Winforms-MessageBoxをMainFormの中央に表示するにはどうすればよいですか?

Winforms-ダイアログボックスをMainFormの中央に表示するにはどうすればよいですか?これは、画面の中央にレンダリングする通常のウィンドウのデフォルトに基づいているのとは対照的です。

私の場合、たとえばコーナーに配置される可能性のある小さなメインフォームがあり、MessageBoxポップアップが離れたところに表示されます。

50
Greg

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。

64
Hans Passant

これは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に、見つけたいウィンドウに付けた一意のキャプション(タイトル)を探すように指示します。

これは、メッセージボックスが画面上に描画される前に発生します。そのため、コードによってアクションがいつ実行されるかを示すグローバルフラグを設定し、一意のキャプションを探す場合、実行するアクションは必ず1回だけ発生します(複数存在する可能性があります)通知機能)。詳細については調べていませんが、CreateWindowでメッセージボックスダイアログに編集ボックスを配置することができました/クローンの豚の背骨にネズミの耳を移植したように見えましたが、うまくいきました。この方法で行うことは、自分で行うよりもはるかに簡単です。

カラス。

編集:右側のウィンドウが処理されるようにするための小さな修正。親のハンドルが全体的に一致していることを確認してください。これで問題ありません。同じプログラムの2つのインスタンスがあったとしても、それは私にとっては効果的です...

2
user1418124

クラスは他の2つの状況に適用できることが証明されました。大きくしたいFolderBrowserDialogがあり、それを親ダイアログの左上(クリックして開くボタンの近く)の近くに表示したかったのです。

CenterWinDialogクラスをコピーして、2つの新しいクラスを作成しました。 1つのクラスはダイアログのサイズを変更し、もう1つのクラスはその位置を親フォームからの特定のオフセットに変更します。これは使用法です:

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

これらは、元のクラスに基づいていた2つのクラスです。

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

独自のメッセージボックスを作成します。フォームとラベルがそれをすべきです。それともグローバル化する必要がありますか?

1
Pedery

自分で作る..

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