
管理人
こんにちは!今回は、メッセージボックスをウィンドウの中央に表示する方法を紹介するよ。

質問者
普通の MessageBox.Show じゃダメなんですか?いつも画面中央に表示されるから、それで十分な気がしますけど。

管理人
いい質問だね!MessageBox.Show はデフォルトで画面の中央に表示されるんだけど、特定のウィンドウの中央に表示させたい場合には使いにくいんだ。例えば、複数のウィンドウを持つアプリケーションで、どのウィンドウに関連するメッセージかを明確にしたいときに便利なんだよ。

質問者
なるほど、それならウィンドウごとにメッセージボックスを中央に表示できると便利ですね。どんな風に実装するんですか?

管理人
実装には少しだけ工夫が必要なんだ。具体的には、Windows API のフックを使って、メッセージボックスが表示される瞬間に位置を調整するんだ。まず、以下のようなコードを書いて、CenterMessageBox クラスを作るんだよ。
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace CenterMessageBoxApp
{
public static class CenterMessageBox
{
/// <summary>
/// オーナーウィンドウのハンドルを保持します。
/// </summary>
private static IntPtr hOwner = IntPtr.Zero;
/// <summary>
/// フックのハンドルを保持します。
/// </summary>
private static IntPtr hHook = IntPtr.Zero;
/// <summary>
/// 指定されたオーナーウィンドウの中央にメッセージボックスを表示します。
/// </summary>
/// <param name="owner">オーナーウィンドウ</param>
/// <param name="text">メッセージボックスに表示するテキスト</param>
/// <param name="caption">メッセージボックスのタイトル</param>
/// <param name="buttons">メッセージボックスに表示するボタン</param>
/// <param name="icon">メッセージボックスに表示するアイコン</param>
/// <returns>ユーザーの選択を示す DialogResult</returns>
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
hOwner = owner.Handle;
IntPtr hInstance = NativeMethods.GetWindowHInstance(hOwner);
IntPtr threadId = NativeMethods.GetCurrentThreadId();
// フックを設定
hHook = NativeMethods.SetWindowsHookEx(new NativeMethods.HOOKPROC(HookProc), hInstance, threadId);
// メッセージボックスの表示
return MessageBox.Show(owner, text, caption, buttons, icon);
}
/// <summary>
/// フックプロシージャ。メッセージボックスをオーナーウィンドウの中央に配置します。
/// </summary>
/// <param name="nCode">フックコード</param>
/// <param name="wParam">メッセージボックスのウィンドウハンドル</param>
/// <param name="lParam">メッセージ情報</param>
/// <returns>次のフックプロシージャの戻り値</returns>
private static IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode == NativeMethods.HCBT_ACTIVATE)
{
// オーナーウィンドウとメッセージボックスの位置を取得
var ownerRect = NativeMethods.GetWindowRect(hOwner);
var msgBoxRect = NativeMethods.GetWindowRect(wParam);
// メッセージボックスをオーナーウィンドウの中央に配置
int x = ownerRect.Left + (ownerRect.Width - msgBoxRect.Width) / 2;
int y = ownerRect.Top + (ownerRect.Height - msgBoxRect.Height) / 2;
NativeMethods.SetWindowPos(wParam, x, y);
// フックの解除
NativeMethods.UnhookWindowsHookEx(hHook);
hHook = IntPtr.Zero;
}
return NativeMethods.CallNextHookEx(hHook, nCode, wParam, lParam);
}
private static class NativeMethods
{
/// <summary>
/// 指定されたウィンドウに関する情報を取得します。
/// </summary>
/// <param name="hWnd">ウィンドウハンドル</param>
/// <param name="nIndex">情報のオフセット</param>
/// <returns>ウィンドウ情報</returns>
[DllImport("user32.dll")]
private static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
/// <summary>
/// 現在のスレッドIDを取得します。
/// </summary>
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentThreadId();
/// <summary>
/// フックを設定します。
/// </summary>
/// <param name="idHook">フックの種類</param>
/// <param name="lpfn">フックプロシージャ</param>
/// <param name="hInstance">インスタンスハンドル</param>
/// <param name="threadId">スレッドID</param>
/// <returns>フックのハンドル</returns>
[DllImport("user32.dll")]
public static extern IntPtr SetWindowsHookEx(int idHook, HOOKPROC lpfn, IntPtr hInstance, IntPtr threadId);
public static IntPtr SetWindowsHookEx(HOOKPROC lpfn, IntPtr hInstance, IntPtr threadId)
=> SetWindowsHookEx(WH_CBT, lpfn, hInstance, threadId);
/// <summary>
/// フックを解除します。
/// </summary>
/// <param name="hHook">フックのハンドル</param>
/// <returns>成功した場合は true、失敗した場合は false</returns>
[DllImport("user32.dll")]
public static extern bool UnhookWindowsHookEx(IntPtr hHook);
/// <summary>
/// 次のフックプロシージャにメッセージを渡します。
/// </summary>
/// <param name="hHook">フックのハンドル</param>
/// <param name="nCode">フックコード</param>
/// <param name="wParam">メッセージパラメータ</param>
/// <param name="lParam">メッセージパラメータ</param>
/// <returns>次のフックプロシージャの戻り値</returns>
[DllImport("user32.dll")]
public static extern IntPtr CallNextHookEx(IntPtr hHook, int nCode, IntPtr wParam, IntPtr lParam);
/// <summary>
/// 指定されたウィンドウの位置を取得します。
/// </summary>
/// <param name="hWnd">ウィンドウハンドル</param>
/// <param name="lpRect">ウィンドウの位置情報</param>
/// <returns>成功した場合は true、失敗した場合は false</returns>
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
/// <summary>
/// ウィンドウの位置とサイズを変更します。
/// </summary>
/// <param name="hWnd">ウィンドウハンドル</param>
/// <param name="hWndInsertAfter">Zオーダー</param>
/// <param name="x">新しいX座標</param>
/// <param name="y">新しいY座標</param>
/// <param name="cx">新しい幅</param>
/// <param name="cy">新しい高さ</param>
/// <param name="uFlags">ウィンドウ配置オプション</param>
/// <returns>成功した場合は true、失敗した場合は false</returns>
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
/// <summary>
/// アプリケーション インスタンスへのハンドルを取得するためのオフセット。
/// </summary>
public const int GWL_HINSTANCE = -6;
/// <summary>
/// CBTフックの識別子。
/// </summary>
public const int WH_CBT = 5;
/// <summary>
/// メッセージボックスがアクティブになるときのフックコード。
/// </summary>
public const int HCBT_ACTIVATE = 5;
/// <summary>
/// フックプロシージャのデリゲート。
/// </summary>
public delegate IntPtr HOOKPROC(int nCode, IntPtr wParam, IntPtr lParam);
/// <summary>
/// 指定されたウィンドウのインスタンスハンドルを取得します。
/// </summary>
/// <param name="hWnd">ウィンドウハンドル</param>
/// <returns>インスタンスハンドル</returns>
public static IntPtr GetWindowHInstance(IntPtr hWnd) => GetWindowLong(hWnd, GWL_HINSTANCE);
/// <summary>
/// ウィンドウの位置を取得します。
/// </summary>
/// <param name="hWnd">ウィンドウハンドル</param>
/// <returns>ウィンドウのRECT構造体</returns>
public static RECT GetWindowRect(IntPtr hWnd)
{
GetWindowRect(hWnd, out RECT rect);
return rect;
}
/// <summary>
/// ウィンドウの位置を指定した座標に移動します。
/// </summary>
/// <param name="hWnd">ウィンドウハンドル</param>
/// <param name="x">新しいX座標</param>
/// <param name="y">新しいY座標</param>
/// <returns>成功した場合は true、失敗した場合は false</returns>
public static bool SetWindowPos(IntPtr hWnd, int x, int y)
{
const uint SWP_NOSIZE = 0x0001;
const uint SWP_NOZORDER = 0x0004;
const uint SWP_NOACTIVATE = 0x0010;
uint flags = SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE;
return SetWindowPos(hWnd, 0, x, y, 0, 0, flags);
}
/// <summary>
/// ウィンドウの位置とサイズを表す構造体。
/// </summary>
public struct RECT
{
public int Left, Top, Right, Bottom;
public int Width => Right - Left;
public int Height => Bottom - Top;
}
}
}
}

質問者
おお、なんだかすごそうですね!でもフックとかって難しそう…。

管理人
確かに、ちょっと難しそうに聞こえるかもしれないね。でも、心配しないで。このコードを使うことで、メッセージボックスを簡単にオーナーウィンドウの中央に表示できるようになるんだよ。

質問者
なるほど!このコードがメッセージボックスの位置を調整しているんですね。これなら、ユーザーにとっても見やすくなりそうです。

管理人
その通り!この方法を使えば、より直感的なユーザーインターフェースを作ることができるよ。ぜひ試してみてね!
コメント