たきるブログ

C#やOracleなどの情報を書いています。

【C#】フォーム終了時のValidating、Validatedイベントを制御する

Visual C# 2012
.NET Framework 4.5

入力エリアコントロールにあるCausesValidationプロパティはデフォルトではtrue。
そのため、何も意識しなければ、Validating、Validatedイベントは走行し、フォーカスを失った時に入力値を検証し、OKならフォーカス移動させる、NGならフォーカス移動させないということが可能だ。

その動作を利用して、フォームを閉じた時に制御を加えよう。

フォームを閉じる時にValidatingの結果に応じて制御を変更したい

FormClosingイベントは、直前にカーソルが存在したコントロールで走行したValidatingイベントのe.Cancel値を引き継いで走行される。
よって、
Validatingイベントの結果に応じて、フォームを閉じる/閉じないを制御する、というのは、以下のようなコードで実装可能。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void textBox1_Validating(object sender, CancelEventArgs e)
        {
            // 何かの入力条件の関係で入力値を拒否する
            if ("hoge".Equals(textBox1.Text))
            {
                MessageBox.Show("入力値が不正です。");
                e.Cancel = true;
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Validatingでエラーだった時、後続処理もせず、フォームを閉じない
            if (e.Cancel)
            {
                return;
            }

            switch (MessageBox.Show("変更されています。保存しますか?", this.Text,
                    MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
            {
                case DialogResult.Cancel:
                    e.Cancel = true;
                    return;

                case DialogResult.No:
                    e.Cancel = false;
                    return;

                default:
                    // 保存処理とかの結果
                    bool result = false;

                    if (!result)
                    {
                        MessageBox.Show("保存に失敗しました。");
                        e.Cancel = true;
                        return;
                    }
                    
                    return;
            }
        }
    }
}

フォームを閉じる時にValidatingを走行させたくない

FormにあるAutoValidateプロパティをDisableにするとValidatingイベントは走行しなくなる。
しかし、FormClosingイベント内で記述しても、それより先にValidatingイベントが走行するため、希望する動作はしない。
そこで、WndProcメソッドをオーバーライドし、フォームが閉じられたというメッセージを取得して制御することで実現可能。
恐らく、これがもっとも簡単な制御方法だ。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void textBox1_Validating(object sender, CancelEventArgs e)
        {
            // 何かの入力条件の関係で入力値を拒否する
            if ("hoge".Equals(textBox1.Text))
            {
                MessageBox.Show("入力値が不正です。");
                e.Cancel = true;
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Validatingの走行有無をデフォルトに戻す
            this.AutoValidate = AutoValidate.Inherit;

            // Validatingでエラーだった時、後続処理もせず、フォームを閉じない
            if (e.Cancel)
            {
                return;
            }

            switch (MessageBox.Show("変更されています。保存しますか?", this.Text,
                    MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
            {
                case DialogResult.Cancel:
                    e.Cancel = true;
                    return;

                case DialogResult.No:
                    e.Cancel = false;
                    return;

                default:
                    // 保存処理とか
                    bool result = false;

                    if (!result)
                    {
                        MessageBox.Show("保存に失敗しました。");
                        e.Cancel = true;
                        return;
                    }
                    
                    return;
            }
        }

        protected override void WndProc(ref Message m)
        {
            // 画面終了のメッセージが届いた時、Validatingが走行しないようにする
            const int WM_SYSCOMMAND = 0x112;
            const int SC_CLOSE = 0xF060;

            if (m.Msg == WM_SYSCOMMAND && m.WParam.ToInt32() == SC_CLOSE)
            {
                this.AutoValidate = AutoValidate.Disable;
            }

            base.WndProc(ref m);
        }
    }
}