C# ShowDialogの使い方を完全解説|モーダル表示・戻り値・画面遷移でつまずかない実装例

はじめに

C#でWindowsデスクトップアプリを作っていると、設定画面、ログイン画面、入力画面、確認画面などを「別ウィンドウとして開き、閉じるまで元の画面に戻らせたくない」場面がよくあります。

そのときに使う代表的なメソッドが ShowDialog です。

ShowDialog を使うと、フォームやウィンドウをモーダル表示できます。モーダル表示とは、表示中の画面を閉じるまで、呼び出し元の画面操作や後続処理を進めない表示方法です。WinFormsの Form.ShowDialog はフォームをモーダルダイアログとして表示し、戻り値として DialogResult を返します。Microsoft Learnでも、ShowDialog は「フォームをモーダルダイアログボックスとして表示する」メソッドとして説明されています。

この記事では、C#の ShowDialog の基本から、Show との違い、戻り値の受け取り方、フォーム間のデータ受け渡し、画面遷移、WPFでの使い方、よくあるエラーまで、実装例を交えて解説します。

1. C#のShowDialogとは?モーダル表示の基本を理解しよう

1-1. ShowDialogでできること

ShowDialog は、別フォームや別ウィンドウを「ダイアログ」として表示するためのメソッドです。

たとえば、メイン画面から設定画面を開き、ユーザーが「OK」または「キャンセル」を押すまでメイン画面の処理を止めたい場合に使います。

WinFormsでは、次のように書きます。

C#
using (var form = new SettingForm())
{
DialogResult result = form.ShowDialog();

if (result == DialogResult.OK)
{
// OKが押された後の処理
}
}

ShowDialog の主な役割は次のとおりです。

  • 子フォームをモーダル表示する

  • 子フォームが閉じられるまで呼び出し元の後続処理を待機する

  • DialogResult.OKDialogResult.Cancel などの戻り値を受け取る

  • 入力画面や確認画面の結果に応じて処理を分岐する

単に別画面を表示するだけでなく、「ユーザーの操作結果を受け取ってから次の処理へ進む」ために使うのが ShowDialog の重要なポイントです。

1-2. モーダル表示とは何か

モーダル表示とは、表示された画面を閉じるまで、呼び出し元の画面を操作できない表示方式です。

たとえば、アプリで「保存しますか?」という確認画面が表示されたとき、その確認画面を閉じるまで元の画面をクリックできないことがあります。これがモーダル表示です。

ShowDialog を使うと、ユーザーは表示されたダイアログに対して何らかの操作を行い、閉じてから元の画面に戻る流れになります。

C#
private void btnOpen_Click(object sender, EventArgs e)
{
var dialog = new SettingForm();
dialog.ShowDialog();

MessageBox.Show("設定画面が閉じられました。");
}

このコードでは、SettingForm が閉じられるまで、MessageBox.Show("設定画面が閉じられました。"); は実行されません。

つまり、ShowDialog は「表示して終わり」ではなく、「閉じるまで待つ」メソッドです。

1-3. ShowDialogを使う代表的な場面

ShowDialog は、ユーザーに何らかの判断や入力をしてもらい、その結果をもとに後続処理を決める場面に向いています。

代表的な利用シーンは次のとおりです。

利用場面ShowDialogが向いている理由
ログイン画面認証が完了するまでメイン画面を表示・操作させたくない
設定画面OKなら設定を反映し、キャンセルなら破棄したい
入力ダイアログ入力値を受け取ってから処理を続けたい
確認画面OK・Cancel・Yes・Noで処理を分けたい
ファイル選択画面選択完了後にファイルパスを受け取りたい

たとえば、ログイン成功時だけメイン画面を表示する場合、ShowDialog は非常に相性が良いです。

C#
using (var loginForm = new LoginForm())
{
if (loginForm.ShowDialog() == DialogResult.OK)
{
Application.Run(new MainForm());
}
}

このように、ShowDialog は「次の処理に進んでよいか」を判断するための画面に適しています。

1-4. ShowDialogとDialogの違いを混同しないための前提知識

ShowDialogDialog という言葉は混同されやすいですが、意味が少し異なります。

Dialog は一般的に「ユーザーと対話するための画面」や「確認・入力用の小さな画面」を指す概念です。一方、ShowDialog は、そのダイアログをモーダル表示するためのメソッドです。

つまり、次のように整理できます。

用語意味
Dialog確認画面・入力画面などの対話用画面の概念
ShowDialogフォームやウィンドウをモーダル表示するメソッド
DialogResultダイアログの操作結果を表す戻り値
Showフォームをモードレス表示するメソッド

「Dialogを表示する」ために ShowDialog を使う、という関係で理解すると混乱しにくくなります。

2. ShowDialogの基本的な使い方

2-1. 別フォームをShowDialogで表示する最小コード

WinFormsで別フォームを ShowDialog で表示する最小コードは次のとおりです。

C#
private void btnOpen_Click(object sender, EventArgs e)
{
Form2 form2 = new Form2();
form2.ShowDialog();
}

これだけで、Form2 をモーダル表示できます。

ただし、この書き方では Form2 の破棄を明示していません。実務では、後述する using を使った書き方がよく使われます。

C#
private void btnOpen_Click(object sender, EventArgs e)
{
using (Form2 form2 = new Form2())
{
form2.ShowDialog();
}
}

フォームは画面リソースを持つため、使い終わったら破棄することが大切です。Microsoft Learnでも、ダイアログとして表示されたフォームは不要になった時点で Dispose を呼び出す必要があると説明されています。

2-2. 親フォームから子フォームを開く基本パターン

親フォームから子フォームを開く場合は、ShowDialog(this) のように親フォームを渡す書き方がよく使われます。

C#
private void btnSetting_Click(object sender, EventArgs e)
{
using (var settingForm = new SettingForm())
{
settingForm.ShowDialog(this);
}
}

this は現在のフォーム、つまり親フォームを表します。

親フォームを指定すると、子フォームがどの画面に属しているかが明確になります。複数ウィンドウのアプリでは、ダイアログが背面に回ったり、どの画面のダイアログなのか分かりにくくなったりすることを防ぎやすくなります。

C#
using (var dialog = new EditUserForm())
{
DialogResult result = dialog.ShowDialog(this);

if (result == DialogResult.OK)
{
// 編集結果を反映する
}
}

ShowDialog() だけでも動作しますが、親子関係を明確にしたい場合は ShowDialog(this) を使うのがおすすめです。

2-3. ShowDialog実行中に呼び出し元の処理が止まる仕組み

ShowDialog を呼び出すと、そのダイアログが閉じられるまで、呼び出し元メソッドの後続コードは実行されません。

C#
private void btnOpen_Click(object sender, EventArgs e)
{
MessageBox.Show("ダイアログを開きます");

using (var form = new Form2())
{
form.ShowDialog();
}

MessageBox.Show("ダイアログが閉じられました");
}

この場合、処理の流れは次のようになります。

  1. 「ダイアログを開きます」と表示される

  2. Form2 が表示される

  3. Form2 を閉じる

  4. 「ダイアログが閉じられました」と表示される

ShowDialog の後に書いた処理は、ダイアログが閉じられるまで実行されません。Microsoft Learnでも、ShowDialog が呼び出されると、ダイアログが閉じられるまで後続コードは実行されないと説明されています。

ただし、これはアプリ全体が完全に停止するという意味ではありません。ダイアログ自体のボタンクリックや入力操作は通常どおり動作します。「呼び出し元の処理が待機する」と理解すると正確です。

2-4. usingを使ってフォームを安全に破棄する書き方

ShowDialog でフォームを開くときは、基本的に次のように using を使うのがおすすめです。

C#
private void btnOpen_Click(object sender, EventArgs e)
{
using (var dialog = new SettingForm())
{
if (dialog.ShowDialog(this) == DialogResult.OK)
{
// OK時の処理
}
}
}

using を使うと、ブロックを抜けたタイミングで自動的に Dispose が呼ばれます。

次のように手動で Dispose する書き方も可能です。

C#
private void btnOpen_Click(object sender, EventArgs e)
{
var dialog = new SettingForm();

try
{
dialog.ShowDialog(this);
}
finally
{
dialog.Dispose();
}
}

ただし、通常は using の方が簡潔で安全です。

特に、ShowDialog で表示したフォームは閉じても内部的には「破棄」ではなく「非表示」に近い扱いになる場合があるため、再利用しないフォームは using で確実に破棄する設計にしておくとトラブルを減らせます。

3. ShowDialogとShowの違い

3-1. ShowDialogはモーダル、Showはモードレス

C#でフォームを表示する方法には、主に ShowDialogShow があります。

大きな違いは、モーダル表示かモードレス表示かです。

メソッド表示方式特徴
ShowDialogモーダル閉じるまで呼び出し元の操作や後続処理を待たせる
Showモードレス表示後も呼び出し元の操作や後続処理が続く

ShowDialog の例です。

C#
private void btnDialog_Click(object sender, EventArgs e)
{
var form = new Form2();
form.ShowDialog();

MessageBox.Show("Form2が閉じられました");
}

Show の例です。

C#
private void btnShow_Click(object sender, EventArgs e)
{
var form = new Form2();
form.Show();

MessageBox.Show("Form2を表示しました");
}

Show の場合、Form2 を閉じる前に次の MessageBox が表示されます。

3-2. 呼び出し元フォームを操作できるかどうかの違い

ShowDialog で開いたフォームが表示されている間、通常は呼び出し元フォームを操作できません。

C#
using (var form = new SettingForm())
{
form.ShowDialog(this);
}

この場合、SettingForm を閉じるまで親フォームのボタンやテキストボックスは操作できません。

一方、Show で表示した場合は、子フォームを表示したまま親フォームも操作できます。

C#
var form = new LogViewerForm();
form.Show();

ログビューア、検索結果画面、プレビュー画面のように「開いたまま他の作業もしたい」画面では Show が向いています。

逆に、ログイン画面、設定画面、確認画面のように「閉じるまで次に進ませたくない」画面では ShowDialog が向いています。

3-3. 戻り値を受け取れるかどうかの違い

WinFormsの ShowDialogDialogResult を返します。DialogResult は、ダイアログの戻り値を示す列挙体です。Microsoft Learnでは、DialogResult はダイアログボックスの戻り値を示す識別子として説明され、OKCancelYesNo などの値が定義されています。

C#
using (var dialog = new ConfirmForm())
{
DialogResult result = dialog.ShowDialog();

if (result == DialogResult.OK)
{
// OK時の処理
}
}

一方、Show は戻り値でユーザーの操作結果を受け取るメソッドではありません。

C#
var form = new Form2();
form.Show();

Show で開いたフォームの結果を受け取りたい場合は、イベント、コールバック、共有モデル、プロパティ参照など別の仕組みを用意する必要があります。

そのため、「閉じた後に結果を受け取って分岐したい」場合は ShowDialog の方がシンプルです。

3-4. ShowDialogとShowの使い分け判断基準

ShowDialogShow は、次の基準で使い分けると分かりやすいです。

判断基準ShowDialogShow
閉じるまで親画面を操作させたくない向いている向いていない
OK・Cancelなどの結果を受け取りたい向いている別実装が必要
入力完了後に次の処理へ進みたい向いている向いていない
画面を開いたまま作業を続けたい向いていない向いている
常時表示する補助画面にしたい向いていない向いている

たとえば、次のように考えると判断しやすくなります。

C#
// 設定画面:閉じるまで待ちたいのでShowDialog
using (var form = new SettingForm())
{
form.ShowDialog(this);
}

// ログ画面:開いたまま作業したいのでShow
var logForm = new LogForm();
logForm.Show();

ShowDialog は便利ですが、何でもモーダルにすると操作性が悪くなります。ユーザーの作業を一時停止させる必要がある画面だけに使うのが基本です。

4. ShowDialogで戻り値を取得する方法

4-1. DialogResultとは何か

DialogResult は、ダイアログがどのような結果で閉じられたかを表す値です。

代表的な値は次のとおりです。

意味
DialogResult.OKOKが押された
DialogResult.Cancelキャンセルされた
DialogResult.Yesはいが押された
DialogResult.Noいいえが押された
DialogResult.Abort中止
DialogResult.Retry再試行
DialogResult.Ignore無視
DialogResult.None結果なし

よく使うのは OKCancel です。

C#
DialogResult result = dialog.ShowDialog();

if (result == DialogResult.OK)
{
// OKの場合
}
else if (result == DialogResult.Cancel)
{
// Cancelの場合
}

DialogResult は、単なる戻り値ではなく「その画面でユーザーがどう判断したか」を表すための値です。

4-2. OK・CancelボタンでDialogResultを返す実装例

子フォームにOKボタンとキャンセルボタンがある場合、ボタンクリック時に DialogResult を設定します。

子フォーム側のコード例です。

C#
public partial class SettingForm : Form
{
public SettingForm()
{
InitializeComponent();
}

private void btnOK_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}

private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}

親フォーム側では、ShowDialog の戻り値を確認します。

C#
private void btnSetting_Click(object sender, EventArgs e)
{
using (var form = new SettingForm())
{
DialogResult result = form.ShowDialog(this);

if (result == DialogResult.OK)
{
MessageBox.Show("設定が保存されました。");
}
else
{
MessageBox.Show("設定はキャンセルされました。");
}
}
}

このように、子フォームで DialogResult を設定し、親フォームでその戻り値を判定するのが基本です。

4-3. ShowDialogの戻り値で処理を分岐する方法

ShowDialog の戻り値は、if 文や switch 文で分岐できます。

C#
using (var dialog = new ConfirmForm())
{
DialogResult result = dialog.ShowDialog(this);

switch (result)
{
case DialogResult.OK:
SaveData();
break;

case DialogResult.Cancel:
MessageBox.Show("処理をキャンセルしました。");
break;
}
}

Yes / No を使う場合は次のように書けます。

C#
using (var dialog = new DeleteConfirmForm())
{
DialogResult result = dialog.ShowDialog(this);

if (result == DialogResult.Yes)
{
DeleteData();
}
else
{
MessageBox.Show("削除を中止しました。");
}
}

ただし、自作フォームでは OK / Cancel の方が意味が明確なことも多いです。

  • 保存するなら OK

  • 中止するなら Cancel

  • 質問に答えるなら Yes / No

というように使い分けると、コードを読む人にも意図が伝わりやすくなります。

4-4. DialogResultがNoneになる原因と対処法

DialogResult.None は「ダイアログの結果が設定されていない」状態を表します。

よくある原因は次のとおりです。

原因対処法
ボタンクリックで DialogResult を設定していないthis.DialogResult = DialogResult.OK; などを設定する
ボタンの DialogResult プロパティが None のままデザイナーまたはコードで設定する
Close() だけ呼んで結果を設定していない閉じる前に DialogResult を設定する
バリデーションで閉じない設計になっている入力OK時だけ DialogResult を設定する

たとえば、次のコードでは戻り値が期待通りにならないことがあります。

C#
private void btnOK_Click(object sender, EventArgs e)
{
this.Close();
}

OK として返したいなら、次のように書きます。

C#
private void btnOK_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}

キャンセルの場合も同様です。

C#
private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}

DialogResult を使うなら、「どの操作でどの結果を返すのか」を明示することが重要です。

4-5. ボタンのDialogResultプロパティを使う方法

WinFormsでは、ボタンの DialogResult プロパティを設定する方法もあります。

C#
public SettingForm()
{
InitializeComponent();

btnOK.DialogResult = DialogResult.OK;
btnCancel.DialogResult = DialogResult.Cancel;

this.AcceptButton = btnOK;
this.CancelButton = btnCancel;
}

この設定をしておくと、OKボタンを押したときに DialogResult.OK、キャンセルボタンを押したときに DialogResult.Cancel を返しやすくなります。

デザイナー上でも、各ボタンの DialogResult プロパティを設定できます。

ボタンDialogResult
OKボタンOK
キャンセルボタンCancel

さらに、フォームの AcceptButtonCancelButton を設定すると、EnterキーやEscキーにも対応できます。

C#
this.AcceptButton = btnOK;
this.CancelButton = btnCancel;

入力ダイアログでは、この設定をしておくと操作性が良くなります。

5. ShowDialogでフォーム間のデータを受け渡しする方法

5-1. 親フォームから子フォームへ値を渡す方法

親フォームから子フォームへ値を渡す方法はいくつかあります。

代表的なのは、プロパティを使う方法です。

C#
using (var form = new EditUserForm())
{
form.UserName = "山田太郎";
form.Email = "yamada@example.com";

form.ShowDialog(this);
}

子フォーム側には、受け取り用のプロパティを用意します。

C#
public partial class EditUserForm : Form
{
public string UserName
{
get => txtUserName.Text;
set => txtUserName.Text = value;
}

public string Email
{
get => txtEmail.Text;
set => txtEmail.Text = value;
}

public EditUserForm()
{
InitializeComponent();
}
}

この方法は、フォーム生成後に値を設定できるため、シンプルで分かりやすいです。

5-2. 子フォームから親フォームへ値を返す方法

子フォームから親フォームへ値を返す場合も、プロパティを使う方法がよく使われます。

子フォーム側です。

C#
public partial class InputNameForm : Form
{
public string InputName
{
get => txtName.Text;
}

public InputNameForm()
{
InitializeComponent();
}

private void btnOK_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtName.Text))
{
MessageBox.Show("名前を入力してください。");
return;
}

this.DialogResult = DialogResult.OK;
this.Close();
}
}

親フォーム側です。

C#
private void btnInput_Click(object sender, EventArgs e)
{
using (var form = new InputNameForm())
{
if (form.ShowDialog(this) == DialogResult.OK)
{
lblName.Text = form.InputName;
}
}
}

ポイントは、親フォーム側で DialogResult.OK を確認してから値を取得することです。

キャンセルされた場合は、入力値を使わないようにします。

5-3. プロパティを使ったデータ受け渡しの実装例

編集画面を例に、プロパティでデータを受け渡しする実装を見てみましょう。

子フォーム EditProductForm です。

C#
public partial class EditProductForm : Form
{
public string ProductName
{
get => txtProductName.Text;
set => txtProductName.Text = value;
}

public int Price
{
get
{
int.TryParse(txtPrice.Text, out int price);
return price;
}
set => txtPrice.Text = value.ToString();
}

public EditProductForm()
{
InitializeComponent();
}

private void btnOK_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtProductName.Text))
{
MessageBox.Show("商品名を入力してください。");
return;
}

if (!int.TryParse(txtPrice.Text, out _))
{
MessageBox.Show("価格は数値で入力してください。");
return;
}

this.DialogResult = DialogResult.OK;
this.Close();
}

private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}

親フォーム側です。

C#
private void btnEdit_Click(object sender, EventArgs e)
{
using (var form = new EditProductForm())
{
form.ProductName = "ノートPC";
form.Price = 120000;

if (form.ShowDialog(this) == DialogResult.OK)
{
string name = form.ProductName;
int price = form.Price;

lblProduct.Text = $"{name}:{price:N0}円";
}
}
}

この実装では、親フォームから初期値を渡し、子フォームで編集し、OKの場合だけ編集後の値を親フォームに反映しています。

5-4. コンストラクタを使ったデータ受け渡しの実装例

初期値が必須の場合は、コンストラクタで渡す方法もあります。

子フォーム側です。

C#
public partial class EditUserForm : Form
{
public string UserName => txtUserName.Text;

public EditUserForm(string userName)
{
InitializeComponent();
txtUserName.Text = userName;
}

private void btnOK_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtUserName.Text))
{
MessageBox.Show("ユーザー名を入力してください。");
return;
}

this.DialogResult = DialogResult.OK;
this.Close();
}
}

親フォーム側です。

C#
private void btnEditUser_Click(object sender, EventArgs e)
{
string currentName = lblUserName.Text;

using (var form = new EditUserForm(currentName))
{
if (form.ShowDialog(this) == DialogResult.OK)
{
lblUserName.Text = form.UserName;
}
}
}

コンストラクタで渡す方法は、「このフォームを開くには必ずこの値が必要」という設計を表現しやすいです。

一方、値が多くなりすぎるとコンストラクタが複雑になります。その場合は、専用のデータクラスを渡す方法もあります。

C#
public class UserEditModel
{
public string UserName { get; set; } = "";
public string Email { get; set; } = "";
}
C#
public EditUserForm(UserEditModel model)
{
InitializeComponent();

txtUserName.Text = model.UserName;
txtEmail.Text = model.Email;
}

5-5. 入力ダイアログとしてShowDialogを使う実装例

ShowDialog は、入力ダイアログを作るときに便利です。

子フォーム InputTextForm を用意します。

C#
public partial class InputTextForm : Form
{
public string InputText => txtInput.Text;

public InputTextForm(string title, string defaultText = "")
{
InitializeComponent();

this.Text = title;
txtInput.Text = defaultText;
}

private void btnOK_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtInput.Text))
{
MessageBox.Show("値を入力してください。");
return;
}

this.DialogResult = DialogResult.OK;
this.Close();
}

private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}

親フォームから呼び出します。

C#
private void btnRename_Click(object sender, EventArgs e)
{
using (var form = new InputTextForm("名前の変更", lblName.Text))
{
if (form.ShowDialog(this) == DialogResult.OK)
{
lblName.Text = form.InputText;
}
}
}

このように作っておけば、名前変更、カテゴリ追加、コメント入力など、さまざまな場面で再利用できます。

6. ShowDialogを使った画面遷移の実装例

6-1. メイン画面から設定画面を開く例

メイン画面から設定画面を開き、OKの場合だけ設定を反映する例です。

C#
private void btnSetting_Click(object sender, EventArgs e)
{
using (var form = new SettingForm())
{
form.UserName = this.CurrentUserName;
form.AutoSave = this.IsAutoSaveEnabled;

if (form.ShowDialog(this) == DialogResult.OK)
{
this.CurrentUserName = form.UserName;
this.IsAutoSaveEnabled = form.AutoSave;

ApplySettings();
}
}
}

子フォーム側では、設定値をプロパティとして公開します。

C#
public partial class SettingForm : Form
{
public string UserName
{
get => txtUserName.Text;
set => txtUserName.Text = value;
}

public bool AutoSave
{
get => chkAutoSave.Checked;
set => chkAutoSave.Checked = value;
}

public SettingForm()
{
InitializeComponent();
}

private void btnOK_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}

private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}

設定画面では、キャンセル時に変更内容を破棄できるように、親フォームの値を直接変更しない設計にすると安全です。

6-2. ログイン画面をShowDialogで表示する例

ログイン画面は、ShowDialog と相性が良い代表例です。

メイン処理の起動前にログイン画面を表示します。

C#
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

using (var loginForm = new LoginForm())
{
if (loginForm.ShowDialog() == DialogResult.OK)
{
Application.Run(new MainForm(loginForm.LoginUserName));
}
}
}

ログインフォーム側です。

C#
public partial class LoginForm : Form
{
public string LoginUserName => txtUserName.Text;

public LoginForm()
{
InitializeComponent();
}

private void btnLogin_Click(object sender, EventArgs e)
{
if (Authenticate(txtUserName.Text, txtPassword.Text))
{
this.DialogResult = DialogResult.OK;
this.Close();
}
else
{
MessageBox.Show("ユーザー名またはパスワードが違います。");
}
}

private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}

private bool Authenticate(string userName, string password)
{
// 実際はDBやAPIなどで認証する
return userName == "admin" && password == "password";
}
}

この実装では、ログイン成功時だけメイン画面を起動します。キャンセルや認証失敗ではメイン画面に進みません。

6-3. 確認画面でOKなら次の処理へ進む例

削除や保存など、取り消しにくい処理の前には確認画面を出すことがあります。

簡単な確認であれば MessageBox.Show でも十分です。

C#
private void btnDelete_Click(object sender, EventArgs e)
{
DialogResult result = MessageBox.Show(
"本当に削除しますか?",
"確認",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Warning);

if (result == DialogResult.OK)
{
DeleteSelectedItem();
}
}

独自デザインの確認画面を使いたい場合は、自作フォームを ShowDialog で表示します。

C#
private void btnDelete_Click(object sender, EventArgs e)
{
using (var form = new DeleteConfirmForm())
{
form.Message = "選択したデータを削除します。よろしいですか?";

if (form.ShowDialog(this) == DialogResult.OK)
{
DeleteSelectedItem();
}
}
}

ShowDialog を使うと、確認画面に詳細情報、チェックボックス、注意文、対象データ一覧などを含められます。

6-4. 子フォームを閉じた後に親フォームを更新する方法

子フォームで編集した内容を親フォームに反映するには、ShowDialog が閉じた後に更新処理を呼びます。

C#
private void btnEdit_Click(object sender, EventArgs e)
{
using (var form = new EditItemForm(selectedItemId))
{
if (form.ShowDialog(this) == DialogResult.OK)
{
LoadItems();
SelectItem(form.EditedItemId);
}
}
}

このように、子フォームで直接親フォームを更新するのではなく、親フォーム側で再読み込みする方が保守しやすくなります。

悪い例です。

C#
// 子フォームから親フォームのコントロールを直接触る
parentForm.dataGridView1.DataSource = newData;

おすすめの例です。

C#
// 親フォーム側で閉じた後に更新する
if (form.ShowDialog(this) == DialogResult.OK)
{
ReloadData();
}

子フォームは「編集する」、親フォームは「一覧を更新する」というように役割を分けると、画面間の依存が減ります。

6-5. 複数フォーム構成でつまずきやすい設計パターン

ShowDialog を多用すると、画面遷移が複雑になることがあります。

たとえば、次のような構成です。

C#
using (var formA = new FormA())
{
if (formA.ShowDialog(this) == DialogResult.OK)
{
using (var formB = new FormB())
{
if (formB.ShowDialog(this) == DialogResult.OK)
{
using (var formC = new FormC())
{
formC.ShowDialog(this);
}
}
}
}
}

このように ShowDialog が入れ子になると、処理の流れが読みにくくなります。

改善するには、画面遷移の制御を専用メソッドに分けます。

C#
private void StartWizard()
{
if (!ShowStep1()) return;
if (!ShowStep2()) return;
ShowStep3();
}

private bool ShowStep1()
{
using (var form = new Step1Form())
{
return form.ShowDialog(this) == DialogResult.OK;
}
}

private bool ShowStep2()
{
using (var form = new Step2Form())
{
return form.ShowDialog(this) == DialogResult.OK;
}
}

private void ShowStep3()
{
using (var form = new Step3Form())
{
form.ShowDialog(this);
}
}

複数画面の流れがある場合は、フォーム同士で直接次のフォームを開かず、親画面や画面遷移用クラスで制御すると見通しが良くなります。

7. ShowDialogでよくあるエラーとつまずきポイント

7-1. フォームが閉じるまで次の処理が実行されない

ShowDialog で最も多い勘違いは、「次の処理が実行されない」というものです。

C#
var form = new Form2();
form.ShowDialog();

DoSomething();

この場合、DoSomething()Form2 が閉じられるまで実行されません。

これはエラーではなく、ShowDialog の仕様です。閉じる前に処理を進めたい場合は、Show を使うか、子フォーム内のイベントで処理を行う必要があります。

C#
var form = new Form2();
form.Show();

DoSomething();

ただし、Show に変更するとモーダルではなくなり、親フォームも操作できるようになります。

「閉じるまで待ちたい」のか、「表示したまま次に進みたい」のかを先に決めることが大切です。

7-2. 親フォームが操作できなくなる

ShowDialog を使うと、親フォームが操作できなくなります。

C#
using (var form = new SettingForm())
{
form.ShowDialog(this);
}

これはモーダル表示の正常な動作です。

もし「子フォームを開いたまま親フォームも操作したい」なら、ShowDialog ではなく Show を使います。

C#
var form = new SearchForm();
form.Show(this);

たとえば、検索画面やログ表示画面などは、親フォームを操作しながら使いたい場合が多いため、Show の方が向いています。

反対に、設定画面やログイン画面では、親フォームを操作できない方が自然です。

7-3. 閉じたフォームを再利用して例外が出る

フォームを一度閉じた後に、同じインスタンスを再利用しようとして問題が起きることがあります。

C#
private Form2 form2 = new Form2();

private void btnOpen_Click(object sender, EventArgs e)
{
form2.ShowDialog();
}

このようにフィールドでフォームを持ち回すと、破棄済みフォームを再表示して例外になることがあります。

基本的には、表示するたびに新しいインスタンスを作る方が安全です。

C#
private void btnOpen_Click(object sender, EventArgs e)
{
using (var form = new Form2())
{
form.ShowDialog(this);
}
}

どうしてもフォームを再利用したい場合は、Close ではなく Hide を使う、破棄状態を確認するなどの設計が必要になります。ただし、通常の入力ダイアログや設定画面では、毎回生成して using で破棄する方がシンプルです。

7-4. DialogResultを設定しても期待通り閉じない

DialogResult を設定しているのに画面が閉じない場合、入力検証やイベント処理でキャンセルされている可能性があります。

たとえば、フォームの FormClosing イベントで閉じる処理をキャンセルしているケースです。

C#
private void SettingForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (HasError())
{
e.Cancel = true;
}
}

この場合、DialogResult.OK を設定しても、e.Cancel = true によってフォームが閉じません。

対処法として、OK時だけバリデーションし、Cancel時や閉じるボタン時の扱いを分けます。

C#
private void btnOK_Click(object sender, EventArgs e)
{
if (!ValidateInput())
{
return;
}

this.DialogResult = DialogResult.OK;
this.Close();
}

private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}

また、ボタンの DialogResult プロパティを設定している場合、FormClosingValidating イベントとの組み合わせで閉じる動作が変わることもあります。

入力検証がある画面では、ボタンの DialogResult プロパティに任せきるより、クリックイベント内で明示的に検証してから閉じる方が分かりやすいです。

7-5. ShowDialogを連続で呼び出すと画面遷移が複雑になる

ShowDialog を連続で呼び出すと、ユーザーにとっても開発者にとっても流れが分かりにくくなることがあります。

C#
if (form1.ShowDialog() == DialogResult.OK)
{
if (form2.ShowDialog() == DialogResult.OK)
{
if (form3.ShowDialog() == DialogResult.OK)
{
Execute();
}
}
}

このようなコードは、画面数が増えるほど保守が難しくなります。

改善例です。

C#
private void ExecuteFlow()
{
if (!ShowInputForm()) return;
if (!ShowConfirmForm()) return;

Execute();
}

さらに画面遷移が多い場合は、ウィザード形式の1画面にまとめる、タブ画面にする、専用の画面遷移クラスを作るなどを検討します。

ShowDialog は便利ですが、複雑な業務フローをすべてモーダル画面の連鎖で表現すると、後から修正しにくくなります。

8. WPFにおけるShowDialogの使い方

8-1. WinFormsとWPFのShowDialogの違い

C#の ShowDialog はWinFormsだけでなく、WPFでも使えます。

ただし、戻り値や設定方法に違いがあります。

項目WinFormsWPF
クラスFormWindow
戻り値DialogResultbool?
OKの表現DialogResult.OKtrue
Cancelの表現DialogResult.Cancelfalse
親の指定ShowDialog(this)Owner プロパティ

WPFの Window.ShowDialog は、ウィンドウを開き、閉じられたときに戻るメソッドです。戻り値は bool? で、処理が受け入れられたかキャンセルされたかを表します。

WinFormsの感覚で DialogResult.OK と書こうとすると、WPFではそのまま使えない点に注意してください。

8-2. WPFのShowDialogはbool?を返す

WPFでは、ShowDialog の戻り値は bool? です。

C#
var window = new SettingWindow();
bool? result = window.ShowDialog();

if (result == true)
{
// OK時の処理
}
else
{
// Cancelまたは閉じるボタン
}

bool?truefalsenull を取り得るNullable型です。

WPFでは、一般的に次のように扱います。

戻り値意味
trueOK、承認、確定
falseCancel、キャンセル
null明示的な結果なし

ただし、実務では result == true の判定を使うことが多いです。

C#
if (window.ShowDialog() == true)
{
Save();
}

この書き方なら、falsenull のどちらも「OKではない」として扱えます。

8-3. WPFでDialogResultを設定する方法

WPFでは、子ウィンドウ側で DialogResulttrue または false を設定します。

C#
private void OKButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}

private void CancelButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
}

WPFでは、DialogResult を設定すると通常はウィンドウが閉じます。

XAMLでは、OKボタンとキャンセルボタンを次のように定義できます。

XML
<StackPanel Orientation="Horizontal">
<Button Content="OK"
Width="80"
Margin="5"
IsDefault="True"
Click="OKButton_Click" />

<Button Content="Cancel"
Width="80"
Margin="5"
IsCancel="True" />
</StackPanel>

コードビハインドです。

C#
private void OKButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(NameTextBox.Text))
{
MessageBox.Show("名前を入力してください。");
return;
}

this.DialogResult = true;
}

Microsoft Learnでは、WPFの DialogResultShowDialog() から返される値であり、ユーザーが承認した場合は true、キャンセルした場合は false として呼び出し元で判断できると説明されています。

8-4. WPFで親ウィンドウを指定するOwnerの使い方

WPFでは、WinFormsのように ShowDialog(this) とは書きません。

親ウィンドウを指定するには、Owner プロパティを設定します。

C#
private void OpenSettingWindow_Click(object sender, RoutedEventArgs e)
{
var window = new SettingWindow();
window.Owner = this;

if (window.ShowDialog() == true)
{
// OK時の処理
}
}

Owner を設定すると、どのウィンドウから開かれたダイアログなのかが明確になります。

複数ウィンドウのWPFアプリでは、Owner を設定していないと、ダイアログが背面に回ったり、親ウィンドウとの関係が分かりにくくなったりすることがあります。

Microsoft Learnでも、WPFの ShowDialog で開いたウィンドウは、開いた側のウィンドウとの関係を自動的には持たないため、Owner プロパティで関係を設定できると説明されています。

8-5. WinFormsのサンプルコードをWPFに置き換えるときの注意点

WinFormsのコードをWPFに置き換えるときは、次の違いに注意します。

WinFormsの例です。

C#
using (var form = new SettingForm())
{
if (form.ShowDialog(this) == DialogResult.OK)
{
ApplySettings();
}
}

WPFでは次のようになります。

C#
var window = new SettingWindow();
window.Owner = this;

if (window.ShowDialog() == true)
{
ApplySettings();
}

主な変更点です。

WinFormsWPF
FormWindow
ShowDialog(this)Owner = this; ShowDialog()
DialogResult.OKtrue
DialogResult.Cancelfalse
using でフォーム破棄通常はウィンドウインスタンスを使い捨てる

WPFでは、MVVMパターンを採用している場合、コードビハインドで直接 ShowDialog を呼ぶか、ダイアログサービスを作るかも設計ポイントになります。

小規模アプリではコードビハインドでも問題ありませんが、画面数が多い場合は、ダイアログ表示の責務をサービス化すると保守しやすくなります。

9. ShowDialogを安全に使うためのベストプラクティス

9-1. フォームの生成と破棄を明確にする

WinFormsで ShowDialog を使うときは、フォームの生成と破棄を明確にしましょう。

基本形は次の書き方です。

C#
using (var form = new SettingForm())
{
if (form.ShowDialog(this) == DialogResult.OK)
{
ApplySettings();
}
}

この書き方なら、フォームを使い終わった後に自動で破棄されます。

避けたいのは、フォームインスタンスを長期間使い回す設計です。

C#
// あまりおすすめしない
private SettingForm settingForm = new SettingForm();

設定画面や入力画面のような一時的なダイアログは、必要なときに作り、閉じたら破棄する方が分かりやすいです。

9-2. 戻り値と入力値の責務を分ける

DialogResult と入力値は、役割を分けて考えます。

  • DialogResult:OKかCancelかを表す

  • プロパティ:入力された値を表す

たとえば、次のようにします。

C#
using (var form = new InputNameForm())
{
if (form.ShowDialog(this) == DialogResult.OK)
{
string name = form.InputName;
RegisterName(name);
}
}

DialogResult に入力値そのものを詰め込むことはできません。

入力値はプロパティや専用モデルで受け取り、DialogResult は「確定したかどうか」の判定に使います。

この役割分担を守ると、コードが読みやすくなります。

9-3. 画面遷移を複雑にしすぎない

ShowDialog は処理の流れを直列に書けるため便利です。

C#
if (form.ShowDialog() == DialogResult.OK)
{
NextProcess();
}

しかし、画面数が増えるとネストが深くなりがちです。

C#
if (formA.ShowDialog() == DialogResult.OK)
{
if (formB.ShowDialog() == DialogResult.OK)
{
if (formC.ShowDialog() == DialogResult.OK)
{
Execute();
}
}
}

このような場合は、画面ごとにメソッドを分けます。

C#
private void ExecuteProcess()
{
if (!ShowFormA()) return;
if (!ShowFormB()) return;
if (!ShowFormC()) return;

Execute();
}

さらに複雑な画面遷移なら、フォーム同士が直接呼び合うのではなく、親画面や画面遷移管理クラスに制御を集約します。

9-4. モーダル表示に向いている画面・向いていない画面

ShowDialog は、すべての画面に向いているわけではありません。

向いている画面は次のようなものです。

  • ログイン画面

  • 設定画面

  • 確認画面

  • 入力ダイアログ

  • 保存前の警告画面

  • 削除確認画面

一方、向いていない画面もあります。

  • 常時表示する一覧画面

  • ログビューア

  • 検索画面

  • プレビュー画面

  • メイン画面と同時に操作したい補助画面

モーダル表示は、ユーザーの操作を一時的に制限します。そのため、必要な場面でだけ使うべきです。

「ユーザーがこの画面を閉じるまで次に進めない方が自然か?」を判断基準にすると、ShowDialog の使いどころを間違えにくくなります。

9-5. 保守しやすいShowDialog実装の考え方

保守しやすい ShowDialog 実装にするには、次の考え方が重要です。

1つ目は、親フォームと子フォームの責務を分けることです。

子フォームは入力や編集を担当し、親フォームは結果を受け取って反映します。

C#
if (form.ShowDialog(this) == DialogResult.OK)
{
UpdateView();
}

2つ目は、子フォームから親フォームのコントロールを直接操作しないことです。

C#
// 避けたい例
parentForm.label1.Text = "更新しました";

代わりに、プロパティで結果を返します。

C#
// おすすめ
string result = form.ResultText;

3つ目は、戻り値の意味を統一することです。

  • 確定は DialogResult.OK

  • 中止は DialogResult.Cancel

  • 質問への回答は Yes / No

このようにルールを揃えると、アプリ全体で ShowDialog の挙動が読みやすくなります。

10. C# ShowDialogに関するよくある質問

10-1. ShowDialogで開いたフォームを閉じるには?

フォーム側で Close() を呼び出します。

C#
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
}

OKやCancelの結果を返したい場合は、閉じる前に DialogResult を設定します。

C#
private void btnOK_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}

キャンセルの場合です。

C#
private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}

ボタンの DialogResult プロパティを設定している場合は、明示的な Close() を書かなくても閉じる動作にできることがあります。ただし、入力チェックを行う画面では、クリックイベント内で検証してから閉じる方が安全です。

10-2. ShowDialogの戻り値はどこで設定する?

WinFormsでは、子フォーム側の DialogResult プロパティに設定します。

C#
this.DialogResult = DialogResult.OK;

親フォーム側では、ShowDialog の戻り値として受け取ります。

C#
using (var form = new InputForm())
{
DialogResult result = form.ShowDialog(this);

if (result == DialogResult.OK)
{
// OK時の処理
}
}

また、ボタンの DialogResult プロパティに設定する方法もあります。

C#
btnOK.DialogResult = DialogResult.OK;
btnCancel.DialogResult = DialogResult.Cancel;

10-3. ShowDialogとMessageBox.Showの違いは?

MessageBox.Show は、メッセージ表示や簡単な確認に使う標準ダイアログです。

C#
DialogResult result = MessageBox.Show(
"保存しますか?",
"確認",
MessageBoxButtons.YesNo);

一方、ShowDialog は自作フォームをモーダル表示するために使います。

C#
using (var form = new SettingForm())
{
form.ShowDialog(this);
}

違いを整理すると、次のようになります。

項目MessageBox.ShowShowDialog
表示内容メッセージ中心自由に設計可能
入力欄基本的になしテキストボックスなど自由に配置可能
デザイン標準自作可能
戻り値DialogResultDialogResult
用途簡単な確認・通知設定・入力・編集など

簡単な確認なら MessageBox.Show、入力欄や複雑なUIが必要なら自作フォームを ShowDialog で開くのが基本です。

10-4. ShowDialogで親フォームに値を反映するには?

子フォームにプロパティを用意し、親フォーム側で ShowDialog の後に値を取得します。

子フォーム側です。

C#
public string InputText => txtInput.Text;

親フォーム側です。

C#
using (var form = new InputTextForm())
{
if (form.ShowDialog(this) == DialogResult.OK)
{
lblResult.Text = form.InputText;
}
}

ポイントは、子フォームから親フォームを直接操作しないことです。

親フォーム側で結果を受け取り、自分自身の画面を更新する方が保守しやすくなります。

10-5. ShowDialogは非同期で使える?

ShowDialog 自体は、基本的に「閉じるまで待つ」同期的な使い方をします。

C#
DialogResult result = form.ShowDialog();

そのため、await form.ShowDialogAsync() のような標準メソッドが常に用意されているわけではありません。

時間のかかる処理をダイアログ内で行う場合は、ShowDialog を非同期化するというより、ダイアログ内の処理を async / await にする設計が一般的です。

C#
private async void btnExecute_Click(object sender, EventArgs e)
{
btnExecute.Enabled = false;

try
{
await ExecuteLongTaskAsync();
this.DialogResult = DialogResult.OK;
this.Close();
}
finally
{
btnExecute.Enabled = true;
}
}

ただし、UIスレッドをブロックする重い処理を直接実行すると画面が固まります。時間のかかる処理は非同期メソッドやバックグラウンド処理に分けることが重要です。

10-6. ShowDialogを使うべき場面と避けるべき場面は?

ShowDialog を使うべき場面は、ユーザーの入力や判断が終わるまで次に進めたくない場合です。

たとえば、次のような場面です。

  • ログインが成功するまでメイン画面に進ませたくない

  • 設定画面でOKが押されたときだけ反映したい

  • 削除前に確認したい

  • 入力値を受け取ってから登録処理を行いたい

一方、避けるべき場面は、ユーザーが複数画面を同時に操作したい場合です。

  • 検索画面を開いたままメイン画面も操作したい

  • ログ画面を常に表示しておきたい

  • プレビュー画面を見ながら編集したい

このような画面では、Show を使ったモードレス表示の方が向いています。

ShowDialog は「処理を一時停止して、ユーザーの判断を待つ」ための仕組みです。便利だからといって多用するのではなく、ユーザーの作業を止める必要がある場面に限定して使うと、使いやすく保守しやすいアプリになります。

まとめ

C#の ShowDialog は、フォームやウィンドウをモーダル表示し、閉じられるまで呼び出し元の後続処理を待機させるためのメソッドです。

WinFormsでは、Form.ShowDialog を使うと DialogResult を戻り値として受け取れます。DialogResult.OKDialogResult.CancelDialogResult.YesDialogResult.No などを使うことで、ユーザーの操作結果に応じた処理分岐ができます。

基本形は次のとおりです。

C#
using (var form = new SettingForm())
{
if (form.ShowDialog(this) == DialogResult.OK)
{
// OK時の処理
}
}

親フォームから子フォームへ値を渡す場合は、プロパティやコンストラクタを使います。子フォームから親フォームへ値を返す場合も、子フォームに公開プロパティを用意し、ShowDialog の戻り値が OK のときだけ取得するのが基本です。

C#
using (var form = new InputForm())
{
if (form.ShowDialog(this) == DialogResult.OK)
{
string value = form.InputValue;
}
}

ShowDialogShow の違いも重要です。ShowDialog はモーダル表示で、閉じるまで親フォームの操作や後続処理を待たせます。一方、Show はモードレス表示で、開いたまま親フォームも操作できます。

WPFでは、ShowDialog の戻り値は DialogResult ではなく bool? です。

C#
var window = new SettingWindow();
window.Owner = this;

if (window.ShowDialog() == true)
{
// OK時の処理
}

ShowDialog は、ログイン画面、設定画面、入力画面、確認画面のように「ユーザーの判断を待ってから次に進む」場面で非常に便利です。

一方で、多用しすぎると画面遷移が複雑になったり、ユーザーの操作を必要以上に制限したりします。フォームの生成と破棄を明確にし、戻り値と入力値の責務を分け、親フォームと子フォームの依存を強くしすぎないことが、保守しやすい ShowDialog 実装のポイントです。