1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#定石 - MessageBox - 親画面中央表示とXボタン非表示

Posted at

はじめに

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 の結果です。

Windows11.png

しかし、Windows Server 系では、MessageBoxButtons.YesNo と MessageBoxButtons.AbortRetryIgnore は、X ボタンは無効化されているのに、無効化されていることが視認できず、Mouse Over にも反応します。(Mouse Over に反応するが、Click はできません)
下記は、Windows 11 24H2 と同様の処理を Windows Server 2025 で実施した結果です。

WindowsServer2025.png

あたかも 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;
  }
<以下省略>

上記対応で、下記表示となります。

MessageBox.png

サンプルコード

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);
1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?