みなさんこんにちは!ひろぽんです!
今回はFormから別のFormに値を持って行ったり、今開いているFormから値を受け取ったりする為の実装についてみていきたいと思います!
[s_ad]

パターン1:MainFormから別Formに値を渡す
💡 「Form の中に Form を表示する MDI 的な切り替え」 をやりたいときは C# Form の中に Form を表示して良い感じに切り替える でまとめてます。値受け渡しの応用編としても使えますよ。
まずはこんな感じのFormを2つ用意します。


でそれぞれ中のコードをこんな感じにします。
namespace BringValueWithForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
var form = new SubForm(textBox1.Text);
form.ShowDialog();
}
}
}
namespace BringValueWithForm.Pattern1
{
public partial class SubForm : Form
{
private string _str;
public SubForm(string str)
{
InitializeComponent();
_str = str;
} private void SubForm_Load(object sender, EventArgs e)
{
label1.Text = $@"MainFormから{_str}の値が来ました。";
}
private void button1_Click(object sender, EventArgs e)
{
}
}
}
これを実行するとこんな感じ!


ちゃんと値がわたっている!!
パターン2:MainFormで別Formから値をもらう
で次はパターン2。
今回はMainFormから開いたSubFormで何かしらの入力がされて、その値をMainFormで受け取るというもの。


こんな感じでパターン1とは反対のFormを用意する。
中身はこんな感じ。
namespace BringValueWithForm.Pattern2
{
public partial class MainForm2 : Form
{
public MainForm2()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
var form = new SubForm2();
form.ShowDialog();
// ここでform1が閉じられたら
label1.Text = $@"SubFormから{form.TextBox1Str}の値が渡ってきました。";
}
}
}
namespace BringValueWithForm.Pattern2
{
public partial class SubForm2 : Form
{
public string TextBox1Str { get; private set; } = "";
public SubForm2()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
TextBox1Str = textBox1.Text;
this.Close();
}
}
}
これを実行するとこんな感じ

立ち上げ時はただのlabelだが。

SubFormでtestと入力されてからSubFormが閉じられると。

こんな感じで値が渡った!
[s_ad]
GitHubでソースを公開しています!
下記リポジトリのBringValueWithFormというフォルダで、今回のソースを公開しています!
リポジトリの詳細は下記を参照ください!

💡 補足: 業務系の現場でよくハマるパターン
俺もこのForm間値受け渡しを実装で何度もやってますが、コードレビューで指摘されるパターンが3つあるんですよね。書いておくと事故が減ります。
① Form2 のコンストラクタ引数を後から追加すると、呼び出し元が全部エラー
「とりあえず引数で渡せばいいか」で public Form2(string name) を作ったあと、後から「やっぱり日付も渡したい」となって public Form2(string name, DateTime date) に変えると、Form2 を呼んでる箇所が全部コンパイルエラーになります。引数追加のたびに修正点が増えるんですよね。回避するなら最初からデータクラスを1つ作って渡す形にしておくと楽です。C#のコールバックとデリゲートの違い で書いたデリゲート渡しも、似た思想で使い分けできます。
② Form2 で入力した値を Form1 のグリッドに即反映する時のタイミングミス
Form2 を ShowDialog() で開いて値を受け取った後、Form1 の DataGridView を即更新したいケース。これ、 ShowDialog() の戻り値で OK/Cancel を判定してから更新しないと、ユーザーがキャンセルしたのに更新が走るバグになります。 Form.ShowDialog と Form.Show の違いと使い分け で深掘りしてますが、業務画面はほぼ ShowDialog 一択です。
③ public フィールドで値共有したら他チームに触られてバグ拡散
「Form2.Result」みたいな public フィールドで結果を渡すパターン、書くのは楽ですが、別チームが「ここから取れるんだ」と覚えてしまい、想定外の場所から参照される事故が起きます。プロパティ + private setter にしておくか、戻り値で渡すか、いずれにせよカプセル化は最初からやっとくと後で泣かない。
❓ よくある質問
Q1. Form1 と Form2 で同じデータを共有したい時、毎回引数で渡すのが面倒です
A. 共有するデータが3項目以上あるなら、専用のデータクラス(DTO)を1つ作って、それを参照渡しするのが定石です。引数1個で済むので Form2 のコンストラクタも増えにくくて、結果的に保守が楽になります。データクラスを DataGridView の DataSource としても流用できる作りにしておくと、現場の幅が広がります。
Q2. Form2 で入力した値を Form1 のグリッドに即反映するには?
A. Form2 を ShowDialog() で開き、戻り値の DialogResult が OK の時だけ Form1 側のメソッド(例: RefreshGrid())を呼ぶ作りにします。Form2 側で this.DialogResult = DialogResult.OK; this.Close(); を発行する型ですね。詳細は Form.ShowDialog vs Form.Show の方に書いてます。
Q3. Form の値受け渡しで public 静的変数を使うのって良くないですか?
A. 動きはしますが、業務系の現場では避けるのが無難です。理由は2つあって、(1)どの画面から参照されてるか追えなくなる、(2)スレッド絡みで競合する。代わりに「DTO を引数で渡す」「イベント/デリゲートで通知する」のどちらかで設計しておくと、後輩が触っても事故りにくいです。
Q4. ShowDialog と Show で値の受け渡し方が違うって本当ですか?
A. 本当です。ShowDialog は「閉じるまで処理がブロックされる」ので、戻り値や閉じた直後にプロパティ参照で値を取れます。Show は「即返ってきて並行動作」なので、イベントやデリゲート経由で通知する形が必要。 使い分けガイド でコード付きでまとめてます。
Q5. DB の値を Form 間で持ち回りたい時のパターンは?
A. DB アクセス層を共通クラスに切り出して、各 Form がそこを叩く形にするのが王道です。Form 間で「DataTable をそのまま渡す」もありですが、メモリ消費が読めなくなるので件数が多いなら避けたい。 DataAdapter で Fill と Update してみる で書いた DataAdapter の使い分けが効きます。
📚 関連記事
- 【C#】Formの中にFormを表示して良い感じに切り替える — MDI 的な画面切り替えをやりたい時の定番パターン
- 【C#】Formから別フォームを表示しメインフォームを切り替える — 親子関係を切り替えながら値を持ち回るパターン
- C# WinForms の Form.ShowDialog と Form.Show の違いと使い分け — 値受け渡しの設計はここで決まる
- 【C#】DataAdapterを使ってFillとUpdateしてみる — Form と DB のデータ往復をやる時に押さえとくやつ
- C#のコールバックとデリゲートの違いはなんなのか — Form 間の通知をデリゲート渡しでやる時の前提知識


コメント