7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

メッセージボックス

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

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);
7
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?