はじめに
C# ソフト開発時に、決まり事として実施していた内容を記載します。
参考情報
下記情報を参考にさせて頂きました。
テスト環境
ここに記載した情報/ソースコードは、Visual Studio Community 2022 を利用した下記プロジェクトで生成したモジュールを Windows 11 24H2 で動作確認しています。
- Windows Forms - .NET Framework 4.8
- Windows Forms - .NET 8
- WPF - .NET Framework 4.8
- WPF - .NET 8
Windows Server 系での確認として、Windows Server 2025 評価版を利用しました。
MessageBox
親画面中央表示
Windows Forms - MessageBox、および、WPF - MessageBox、双方ともに、画面中央に表示されます。このため、親画面の表示の位置を端にずらしている場合、MessageBox が、親画面から外れて表示されてしまい、みっともない状態となってしまいます。
MessageBox は、親画面の中央表示でないとダメですよね。
本件については、先駆者がいるので、ありがたく利用させて頂いています。
Xボタン非表示
MessageBoxButtons.YesNo と MessageBoxButtons.AbortRetryIgnore については、ウィンドウタイトルバー右端 X ボタンは無効化されます。
上記は Windows Forms の場合です。
WPF の場合は、MessageBoxButton.YesNo が対象です。
WPF では、AbortRetryIgnore は存在しません。
以降についても、Windows Forms としての情報を記載します。
Windows 11 24H2 で、MessageBoxButtons.OK と MessageBoxButtons.YesNo それぞれの表示と、X ボタン Mouse Over の結果です。
しかし、Windows Server 系では、MessageBoxButtons.YesNo と MessageBoxButtons.AbortRetryIgnore は、X ボタンは無効化されているのに、無効化されていることが視認できず、Mouse Over にも反応します。(Mouse Over に反応するが、Click はできません)
下記は、Windows 11 24H2 と同様の処理を Windows Server 2025 で実施した結果です。
あたかも X ボタンが有効に見えるこの挙動を抑止するために、MessageBoxButtons.YesNo と MessageBoxButtons.AbortRetryIgnore の場合、X ボタン(SYSMENU)非表示を追加するケースもありました。
親画面中央表示ソースコードに対する、具体的な修正内容を以下に記載します。
WIN32API 定義に SetWindowLong、GWL_STYLE、WS_SYSMENU を追加します。
#region defines
private static class NativeMethods
{
// Xボタン非表示
[DllImport("user32.dll")]
public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, long dwLong);
<中略>
public const int GWL_HINSTANCE = (-6);
public const int WH_CBT = 5;
public const int HCBT_ACTIVATE = 5;
public const int SWP_NOSIZE = 0x0001;
public const int SWP_NOZORDER = 0x0004;
public const int SWP_NOACTIVATE = 0x0010;
public const int GWL_STYLE = (-16); // Xボタン非表示
public const int WS_SYSMENU = 0x00080000; // Xボタン非表示
}
内部変数として、HookButtons を追加します。
#region fields
private readonly IWin32Window Owner;
private IntPtr HookHandle = IntPtr.Zero;
private MessageBoxButtons HookButtons; // Xボタン非表示
#endregion
Show メソッドで HookButtons に MessageBoxButtons の値を保持します。
#region methods
private DialogResult Show(
string text,
string caption,
MessageBoxButtons buttons,
MessageBoxIcon icon,
MessageBoxDefaultButton defaultButton)
{
IntPtr hInstance = NativeMethods.GetWindowLong(this.Owner.Handle,
NativeMethods.GWL_HINSTANCE);
IntPtr threadId = NativeMethods.GetCurrentThreadId();
this.HookHandle = NativeMethods.SetWindowsHookEx(NativeMethods.WH_CBT,
this.HookProc, hInstance, threadId);
this.HookButtons = buttons; // Xボタン非表示
return MessageBox.Show(this.Owner, text, caption, buttons, icon, defaultButton);
}
HookProc メソッドに対して、SYSMENU 非表示するコードを追加します。
private IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode == NativeMethods.HCBT_ACTIVATE)
{
<中略>
NativeMethods.SetWindowPos(wParam, 0, x, y, 0, 0,
NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE);
// Xボタン非表示
if ((this.HookButtons == MessageBoxButtons.YesNo)
|| (this.HookButtons == MessageBoxButtons.AbortRetryIgnore))
{
long style = (long)NativeMethods.GetWindowLong(wParam, NativeMethods.GWL_STYLE);
NativeMethods.SetWindowLong(wParam, NativeMethods.GWL_STYLE,
style & ~NativeMethods.WS_SYSMENU);
}
try
{
return NativeMethods.CallNextHookEx(this.HookHandle, nCode, wParam, lParam);
}
finally
{
NativeMethods.UnhookWindowsHookEx(this.HookHandle);
this.HookHandle = IntPtr.Zero;
}
<以下省略>
上記対応で、下記表示となります。
サンプルコード
Windows Forms
画面中央表示、X ボタン無効化した CenterMessageBox を GitHub で公開します。
- MessageBox 親画面中央表示とXボタン非表示
- CenterMessageBox-WindowsForms.cs
基本的に、参考ソースは、そのままにしています。
MessageBox.Show 引数「MessageBoxOptions options」「string helpFilePath」「HelpNavigator navigator」「object param」「bool displayHelpButton」「string keyword」については、利用することもなかったので、追加していません。
必要に応じて、追加してください。
利用方法を記載します。
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"OK", MessageBoxButtons.OK, MessageBoxIcon.Information);
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"YesNo", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"AbortRetryIgnore", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Question);
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"OKCancel", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"RetryCancel", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question);
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"YesNoCancel", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
// .NET 8 の場合、下記コメントを外す
// MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
// "CancelTryContinue", MessageBoxButtons.CancelTryContinue, MessageBoxIcon.Question);
WPF
WPF の場合、UI ライブラリ利用、NuGet / GitHub などで公開されている MessageBox を利用して、標準 MessageBox をそのまま利用することは少ないと思いますが、、、
WPF 用に修正した CenterMessageBox を GitHub で公開します。
- MessageBox 親画面中央表示とXボタン非表示
- CenterMessageBox-WPF.cs
.NET Framework の場合、「System.Drawing」参照追加が必要です。
WPF 用に修正した部分について、コメント付与すると、可読性が悪くなりそうだったので、コメント付与していません。修正部分を確認したい場合は、双方のソース差分を確認してください。
MessageBox.Show 引数「MessageBoxOptions options」については、未対応です。
必要に応じて、追加してください。
利用方法を記載します。
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"OK", MessageBoxButton.OK, MessageBoxImage.Information);
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"YesNo", MessageBoxButton.YesNo, MessageBoxImage.Question);
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"OKCancel", MessageBoxButton.OKCancel, MessageBoxImage.Question);
MyTools.CenterMessageBox.Show(this, "MessageBox 確認です",
"YesNoCancel", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);