メッセージボックスをオーナーフォームの中央に表示


メッセージボックス

画面中央にしか表示できない。。

MessageBox.Show だと画面中央にメッセージボックスが表示され、オーナーフォーム (親ウィンドウ) の中央に表示するためのパラメーターは存在しない。

親の中央に表示するためのクラス

オーナーフォームの中央に表示するには、Win32 API を使う必要がある。

CustomMessageBox.cs
static class CustomMessageBox
{
    static class Win32Native
    {
        #region GetWindowLong
        [DllImport("user32.dll")]
        static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
        const int GWL_HINSTANCE = -6;

        public static IntPtr GetWindowHInstance(IntPtr hWnd) => GetWindowLong(hWnd, GWL_HINSTANCE);
        #endregion

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetCurrentThreadId();

        #region WindowsHookEx
        [DllImport("user32.dll")]
        static extern IntPtr SetWindowsHookEx(int idHook, HOOKPROC lpfn, IntPtr hInstance, IntPtr threadId);
        const int WH_CBT = 5;
        public static IntPtr SetWindowsHookEx(HOOKPROC lpfn, IntPtr hInstance, IntPtr threadId) => SetWindowsHookEx(WH_CBT, lpfn, hInstance, threadId);

        [DllImport("user32.dll")]
        public static extern bool UnhookWindowsHookEx(IntPtr hHook);
        [DllImport("user32.dll")]
        public static extern IntPtr CallNextHookEx(IntPtr hHook, int nCode, IntPtr wParam, IntPtr lParam);

        public delegate IntPtr HOOKPROC(int nCode, IntPtr wParam, IntPtr lParam);
        public const int HCBT_ACTIVATE = 5;
        #endregion

        #region GetWindowRect
        [DllImport("user32.dll")]
        static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
        public struct RECT
        {
            public int Left, Top, Right, Bottom;
            public int Width => this.Right - this.Left;
            public int Height => this.Bottom - this.Top;
        }

        public static RECT GetWindowRect(IntPtr hWnd)
        {
            RECT rc;
            GetWindowRect(hWnd, out rc);
            return rc;
        }
        #endregion

        #region SetWindowPos
        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
        const uint SWP_NOSIZE = 0x0001;
        const uint SWP_NOZORDER = 0x0004;
        const uint SWP_NOACTIVATE = 0x0010;

        public static bool SetWindowPos(IntPtr hWnd, int x, int y)
        {
            var flags = SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE;
            return SetWindowPos(hWnd, 0, x, y, 0, 0, flags);
        }
        #endregion
    }

    public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
    {
        hOwner = owner.Handle;
        var hInstance = Win32Native.GetWindowHInstance(owner.Handle);
        var threadId = Win32Native.GetCurrentThreadId();
        hHook = Win32Native.SetWindowsHookEx(new Win32Native.HOOKPROC(HookProc), hInstance, threadId);
        return MessageBox.Show(owner, text, caption, buttons, icon);
    }

    private static IntPtr hOwner = (IntPtr)0;
    private static IntPtr hHook = (IntPtr)0;
    private static IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode == Win32Native.HCBT_ACTIVATE)
        {
            // オーナーウィンドウとメッセージボックスの領域を取得
            var rcOwner = Win32Native.GetWindowRect(hOwner);
            var rcMsgBox = Win32Native.GetWindowRect(wParam);

            // メッセージボックスをオーナーウィンドウの中央位置に移動
            var x = rcOwner.Left + (rcOwner.Width - rcMsgBox.Width) / 2;
            var y = rcOwner.Top + (rcOwner.Height - rcMsgBox.Height) / 2;
            Win32Native.SetWindowPos(wParam, x, y);

            // フックを解除
            Win32Native.UnhookWindowsHookEx(hHook);
            hHook = (IntPtr)0;
        }
        return Win32Native.CallNextHookEx(hHook, nCode, wParam, lParam);
    }
}

使用方法

通常の MessageBox と同じく、CustomMessageBox クラスの Show メソッドを呼ぶ。

CustomMessageBox.Show(owner, "メッセージ", "タイトル", MessageBoxButtons.OK, MessageBoxIcon.Information);