動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

WinForms の入力チェックを ErrorProvider でスマートに — Validating と e.Cancel の落とし穴

バイブス父さん
現役の業務SE
2026年7月12日14 min read
WinForms の入力チェックを ErrorProvider でスマートに — Validating と e.Cancel の落とし穴

みなさんこんにちは!ヒロポンです!

業務フォームの入力チェック、MessageBox の連打になってませんか??

必須項目が空 → 「名前を入力してください」でポンッ。日付がおかしい → 「日付の形式が不正です」でまたポンッ。

OK を押しては、次のエラーで止められる。使う側からすると、地味にストレスなんですよね。

これ、ErrorProviderValidating イベントを使うと、入力欄の横に赤いアイコンとツールチップでスマートに出せます。 MessageBox で作業を止めなくて済む。

今回は「WinForms の入力チェックを ErrorProvider でスマートにする方法」を、コピペで動くコードでまとめました。

あと Validatinge.Cancelフォームが閉じられなくなる詰み方。ここが一番の落とし穴なので、しっかり開示します。

💡 メールや電話番号の形式チェックそのものは C# 正規表現の業務系入力検証5パターン にまとめてます。今回はその検証結果を画面にどう出すか、ErrorProvider 側の話です。

結論: ErrorProvider + Validating で入力欄の横に赤アイコンを出す

先に答えです。やることは2つ。

  1. フォームに ErrorProvider を1つ置く
  2. 各入力欄の Validating イベントで、NG なら SetError、OK なら空を SetError
// フォームに ErrorProvider を1つ用意 (デザイナで置いてもOK)
private ErrorProvider errorProvider1 = new ErrorProvider();

// 名前欄の Validating イベント
private void txtName_Validating(object sender, CancelEventArgs e)
{
    if (string.IsNullOrWhiteSpace(txtName.Text))
        errorProvider1.SetError(txtName, "名前は必須です");   // 横に赤アイコン + ツールチップ
    else
        errorProvider1.SetError(txtName, "");                 // 空を渡すとアイコンが消える
}

これだけで、名前欄が空のままフォーカスが外れると、欄の右に赤いアイコンが出ます。マウスを乗せると「名前は必須です」のツールチップ。いい感じにスマート。

ここまでで「あとは各欄に付けるだけ」と分かった人は、ページを閉じてもらってもかまいません! 以下は一括検証のやり方と、e.Cancel の詰み方の話です。

そもそも: ErrorProvider と Validating の役割

ErrorProvider は、コントロールの横にエラーアイコンとツールチップを表示するコンポーネント です。フォームに1個置いておけば、どのコントロールに対しても SetError でエラーを出し分けられます。

Validating は、コントロールからフォーカスが外れる直前に発火するイベント です。「入力を終えて次の欄に移ろうとした瞬間」に呼ばれる。だから入力チェックの定位置になります。

この2つを組み合わせると、「入力し終わった欄から順にチェックして、ダメなら横に赤マーク」という自然な検証ができます。

MessageBox みたいに作業を止めない。ここがポイントです。

最短実装(コピペで動く)

ステップ1: Validating で SetError する

各入力欄の Validating に検証を書きます。日付欄ならこんな感じ。

private void txtDate_Validating(object sender, CancelEventArgs e)
{
    if (!DateTime.TryParse(txtDate.Text, out _))
        errorProvider1.SetError(txtDate, "日付の形式が正しくありません");
    else
        errorProvider1.SetError(txtDate, "");
}

DateTime.TryParse で弾いて、NG なら SetError、OK なら空を渡してクリア。

この「OK の時に空を SetError してクリアする」を忘れると、一度出た赤マークが、直しても消えません。そこだけ注意です。

ステップ2: 保存前に ValidateChildren で一括検証

項目ごとの検証は上でできます。ただ、保存ボタンを押した時に「全部まとめてチェック」したいですよね。そこで ValidateChildren です。

private void btnSave_Click(object sender, EventArgs e)
{
    // フォーム上の全コントロールの Validating をまとめて実行
    if (this.ValidateChildren())
        SaveData();                              // 全部OKなら保存
    else
        MessageBox.Show("入力エラーがあります。赤いマークの欄を確認してください。");
}

this.ValidateChildren() を呼ぶと、フォーム上の全コントロールの Validating が走ります。1つでもエラー(e.Cancel = true)があれば false が返る。

保存直前の最終チェックに、ちょうどいいです。

ハマりポイント: 知らないと詰む3つ

ここが本番です。特に①は、私も一度これで冷や汗かきました。

① e.Cancel = true でフォーカスが抜けられなくなる「詰み」

Validatinge.Cancel = true は、検証に失敗したコントロールからフォーカスを移動させない 効果があります。一見「入力を強制できて便利」に見えるんですよね。

ん? 便利じゃん、と思いきや、これが罠。

private void txtName_Validating(object sender, CancelEventArgs e)
{
    if (string.IsNullOrWhiteSpace(txtName.Text))
    {
        errorProvider1.SetError(txtName, "名前は必須です");
        e.Cancel = true;   // ← これを入れると txtName から出られなくなる
    }
}

これ、名前欄が空のままだと 他のどの欄にも移れず、フォームの閉じるボタンすら効かなくなります。 ユーザーは「名前を入れるまでフォームを閉じられない」状態に閉じ込められる。

私も昔これをやって、テスト中に私のフォームが閉じられなくなって、タスクマネージャーから落とすハメになりました。自作のフォームに、作った本人が閉じ込められる。まさに詰み。笑えないですよね??

閉じるボタンやキャンセルボタンまで検証に巻き込まれるのが原因です。

対策は2つ。閉じる・キャンセル系のボタンは CausesValidation = false にする。もう1つは、そもそも e.Cancel を使わず SetError だけで表示して、保存時に ValidateChildren で最終判定する設計にする。

後者のほうが素直で、私は基本こっちにしてます。

② CausesValidation を切り忘れて、キャンセルできない

①の対策の続き。CausesValidation は、そのコントロールにフォーカスが移る時に、直前のコントロールの Validating を発火させるか を決めるプロパティです。既定は true

// 閉じる・キャンセルボタンは検証をトリガーさせない
btnCancel.CausesValidation = false;
btnClose.CausesValidation  = false;

これを切っておかないと、入力途中で「やっぱりキャンセル」を押したくても、直前の欄の Validating が走って e.Cancel = true に引っかかります。キャンセルボタンまで到達できない。

キャンセル・閉じる系は CausesValidation = false。これを覚えておくと安全です。

③ Validating はフォーカスが動かないと発火しない

Validating は「フォーカスが外れる時」に発火するイベントです。つまり、入力欄に一度もフォーカスが当たらなかったり、当たったまま保存ボタンを押したりすると発火しない ことがあります。

だから「項目ごとの Validating に任せきり」だと、ユーザーが触ってない欄はノーチェックで通る。

ここを埋めるのが、さっきの ValidateChildren です。保存時に ValidateChildren を1回通す。これで「触ってない必須欄」も、いい感じに漏れなく拾えます。

現場メモ: MessageBox 連打から ErrorProvider に寄せると何が変わるか

昔の業務フォームって、検証エラーを全部 MessageBox で出してるやつ、多いんですよね。1項目ずつダイアログが出て、OK を押しては次で止められる。あれ、入力する側からすると本当にしんどい。

ErrorProvider に寄せると、エラーが画面に出っぱなしになります。赤マークの欄を上から順に直していけばいい。

どこが悪いか一覧で見える。直す側の負担が、全然違います。

もちろん MessageBox が悪いわけじゃなくて、使いどころの問題 です。項目単位の指摘は ErrorProvider、「保存に失敗しました」みたいな操作全体の結果は MessageBox。役割を分けると、フォームがグッと使いやすくなります。

検証方式の使い分け

3つの方式を、どういう時に使うかで並べるとこうなります。

WinForms の入力検証を Validating+SetError / ValidateChildren / e.Cancel=true の3方式で、エラー表示・フォーカス移動・一括検証・閉じるボタンの可否で比較した表

見て分かる通り、e.Cancel = true は「閉じるボタンが押せる」が要注意(CausesValidation = false が前提)。

基本は Validating + SetError で表示 → 保存時に ValidateChildren で一括判定。この組み合わせが安全で使いやすいです。

処理の流れ

Validating が発火してから、フォーカスが移れるか止まるかの分岐を図にすると、こうなります。

別のコントロールへフォーカス移動すると Validating が発火し、入力が正しければ SetError で空を設定してフォーカス移動、NG なら SetError でエラー表示し e.Cancel が true ならフォーカスが移れず詰みの危険、false ならエラー表示のまま移動できる分岐フロー

まとめ

  • ErrorProvider を1個置いて、各欄の ValidatingSetError。NG は文言、OK は空文字でクリア
  • 保存時は ValidateChildren で全欄をまとめて検証する
  • e.Cancel = trueフォーカスが抜けられなくなる詰みの元。使うなら閉じる・キャンセルは CausesValidation = false 必須
  • 基本は「Validating + SetError で表示 → 保存時 ValidateChildren で一括判定」が安全

入力検証を ErrorProvider に寄せると、MessageBox 連打のストレスが消えて、フォームが一気に使いやすくなります!!

ユーザーからの「この画面、入力しづらい」が減るだけでも、作った側としては報われますね。

動作確認メモ: この記事の C# コードは .NET Framework 4.7.2 / C# 7.3 相当(Mono)でコンパイル・型解決を確認済みです。ただし ErrorProvider の赤アイコン表示や e.Cancel = true のフォーカス挙動は GUI 依存のため、最終的な見た目・動作は実環境(Windows + Visual Studio)でお試しください。

よくある質問

Q1. WinForms で入力欄の横にエラーマークを出すには?

ErrorProvider コンポーネントをフォームに置き、Validating イベントの中で errorProvider1.SetError(コントロール, "メッセージ") を呼びます。エラーが無い時は空文字を SetError するとマークが消えます。赤いアイコンとツールチップが入力欄の右に出るので、どこが悪いか一目で分かります。

Q2. Validating で e.Cancel=true にするとどうなりますか?

検証に失敗したコントロールからフォーカスが移れなくなります。他の欄も押せず、フォームの閉じるボタンすら効かなくなる「詰み」に陥ることがあります。閉じる・キャンセル系のボタンは CausesValidation = false にするか、e.Cancel を使わず SetError だけで表示する設計が安全です。私はこの詰みでフォームを強制終了する羽目になったので、基本は後者です。

Q3. 複数の入力欄をまとめて検証するには?

保存ボタンのクリック時などに this.ValidateChildren() を呼ぶと、フォーム上の全コントロールの Validating イベントが実行されます。1つでも e.Cancel = true があれば false が返るので、if (ValidateChildren()) で一括判定できます。ユーザーが触っていない必須欄も、これで漏れなくチェックできます。

Q4. ErrorProvider と MessageBox はどう使い分けますか?

項目ごとのリアルタイムな指摘は ErrorProvider が向きます。入力欄の横に出るので、どこが悪いか一目で分かります。MessageBox は「保存に失敗しました」のような操作全体の結果通知に絞ると、ダイアログ連打のストレスが減ります。役割を分けるのがコツです。

次に読むべき記事

以上!

同じように MessageBox 連打のフォームで消耗してる人いたら、どんどんシェア待ってるぜ!!


執筆者

バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SIer の正社員からフリーランスに転じ、 複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。

🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る


この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について