none
C#2008 コントロールのValidatingイベントを発生させたい RRS feed

  • 質問

  • お世話になります。
    皆様の知恵をお借りさせてください。
    よろしくお願いいたします。

    【環境】
    C#2008 WindowFormApp
    テキストコントロール(複数)を使用
    ボタンコントロールを使用

    【現象】
    ボタンを押したときにテキストにフォーカスを移動したいが、
    前回コントロール(CausesValidation = true のコントロール)の
    Validating イベントが発生する。フォーカスの移動だけ行いたい。

    btnSetFocus_ClickイベントでCausesValidationの値を変更して
    前回コントロールの Validating イベント回避した。
    移動したコントロールのValidating イベントが発生しなくなってしまった。

    【サンプルソース】

            private void btnSetFocus_Click(object sender, EventArgs e)
            {
                //一時的にCausesValidation=false
                bool b = ctxTest.CausesValidation;

                ctxTest.CausesValidation = false;
                ctxTest.Select();

                ctxTest.CausesValidation = b;

                Console.Write("btnSetFocus_Click" + "\n");
                Console.Write("ctxTest.CanFocus=" + ctxTest.CanFocus + "\n");
                Console.Write("ctxTest.Focused=" + ctxTest.Focused + "\n");
                Console.Write("this.ActiveControl =" + this.ActiveControl.Name + "\n");
            }

            private void btnValidate_Click(object sender, EventArgs e)
            {Console.Write("btnValidate_Click" + "\n");}

            private void ctxTest_Validating(object sender, System.ComponentModel.CancelEventArgs e)
            {Console.Write("ctxTest_Validating" + "\n");}
    2013年1月30日 6:49

すべての返信

  • ctxTestはテキストボックスでしょうか? だとして、テキストボックスはこのctxTestしか見当たりません。このテキストボックスに移動した際に、このテキストボックスのValidatingイベントを抑制したいということでしょうか? であれば、特に
    ctxTest.CausesValidation を falseにしなくてもValidatingイベントは発生しません。つまり、以下で良いことになります。

    private void btnSetFocus_Click(object sender, EventArgs e)
    {
        ctxTest.Select();
     
        Console.Write("btnSetFocus_Click" + "\n");
        Console.Write("ctxTest.CanFocus=" + ctxTest.CanFocus + "\n");
        Console.Write("ctxTest.Focused=" + ctxTest.Focused + "\n");
        Console.Write("this.ActiveControl =" + this.ActiveControl.Name + "\n");
    }

    前回コントロールが何で、移動したコントロールが何かを明示された方が良いと思います。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2013年1月30日 7:47
    モデレータ
  • ご返答ありがとうございます。

    ctxTestはテキストボックスになります。
    説明足らずで申し訳ございません。

    テキストボックスが4つ並んでいると仮定した場合、

    テキストボックス1(CausesValidation = true)
    テキストボックス2(CausesValidation = false)
    テキストボックス3(CausesValidation = false)
    テキストボックス4(CausesValidation = true)

    ①テキストボックス1からテキストボックス2へフォーカス移動
    ②テキストボックス2からテキストボックス3へフォーカス移動
    ③テキストボックス3からテキストボックス4へフォーカス移動

    このときにテキストボックス1のValidatingイベントを抑制したいです。 

    2013年1月31日 1:31
  • CausesValidationプロパティの意味を勘違いされているかもしれません。CausesValidation = trueになっているコントロールは、そのフォームの検証システムに参加するということを意味します。この検証システムに参加しているどれかのコントロールにフォーカスが移った時点で、この検証システムに参加している全てのコントロールで検証が発動します。つまり、Validatingイベントが発生します。

    フォーカスへの移動をButtonでされているのでしたら、ButtonのCausesValidationをfalseにすれば、そのButtonを押しても検証システムは発動しません。よって、テキストボックスのValidatingイベントが発生しないことになります。
    ButtonになぜCausesValidationがあるのか不思議に思われたかもしれませんが、検証システムに参加するかどうかを制御していると考えれば納得がいかれると思います。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2013年1月31日 4:29
    モデレータ
  • 上手く説明できず、申し訳ありません。


     テキストボックス1(CausesValidation = true)
      ↓
     テキストボックス2(CausesValidation = false)
      ↓
     テキストボックス3(CausesValidation = false)

    と、コントロールを移動した後に

    「ボタン(CausesValidation = false)」のクリック処理(操作)で

    「テキストボックス4(CausesValidation = true)」

    にフォーカスを移動させたいです。

    その際、「テキストボックス1(CausesValidation = true)」の
    Validatingイベントは発生させたくありません。

    ただし、
    「テキストボックス1(CausesValidation = true)」から
    「テキストボックス4(CausesValidation = true)」に直接移動したときには、
    「テキストボックス1(CausesValidation = true)」のValidatingイベントを走らせたいので
    「テキストボックス4」のCausesValidationプロパティはtrueにしてあります。

    そこで、
    「ボタン(CausesValidation = false)」のクリック処理(操作)では
    「テキストボックス4」のCausesValidationプロパティを一旦、falseに変更してから
    「テキストボックス4」に対してフォーカスをセットし、
    その後、「テキストボックス4」のCausesValidationプロパティをtrueに戻すような
    処理を記載しました。

    すると、上の問題は解決できたのですが
    新たな問題として
    「ボタン(CausesValidation = false)」のクリック処理(操作)で
    「テキストボックス4」にフォーカスをセットした直後に
    「テキストボックス4」から
    「テキストボックス1(CausesValidation = true)」にフォーカス移動しても
    「テキストボックス4」のValidatingイベントが発生なくなってしまいました。

    「ボタン(CausesValidation = false)」のクリック処理(操作)でなく
    手動で「テキストボックス4」を選択し、
    「テキストボックス1(CausesValidation = true)」にフォーカス移動したときは
    「テキストボックス4」のValidatingイベントが、きちんと発生します。

    このような感じで
    「ボタン(CausesValidation = false)」のクリック処理(操作)で
    「テキストボックス4」にフォーカスをセットした直後にも
    「テキストボックス4」から
    「テキストボックス1(CausesValidation = true)」にフォーカス移動したときは
    「テキストボックス4」のValidatingイベントが発生するようにするには
    どうしたら良いでしょうか?
    2013年1月31日 6:13
  • 現象を確認しました。
    おそらく、フォーカスが移った際にCausesValidation =
    trueであるかどうかを判断し、検証システムに参加させているのだと思います。よって、それ以外のタイミングでCausesValidation =
    trueにしても、ただ単にCausesValidationプロパティをtrueにしているだけで、何も起こらないのでしょう。
    何か良い方法があるかもしれませんが、とりあえず以下のようにフラグで判断すれば実現できると思います。
    的確な回答でないかもしれませんし、このようなコードは既にお考えかもしれませんが・・・

    bool validation有効 = true;
    
    private void btnSetFocus_Click(object sender, EventArgs e)
    {
        validation有効 = false;
        
        ctxTest.Select();
    
        validation有効 = true;
    
        Console.Write("btnSetFocus_Click" + "\n");
        Console.Write("ctxTest.CanFocus=" + ctxTest.CanFocus + "\n");
        Console.Write("ctxTest.Focused=" + ctxTest.Focused + "\n");
        Console.Write("this.ActiveControl =" + this.ActiveControl.Name + "\n");
    }
    
    private void btnValidate_Click(object sender, EventArgs e)
    {Console.Write("btnValidate_Click" + "\n");}
    
    private void テキストボックス1_Validating(object sender, System.ComponentModel.CancelEventArgs e)
    {if (validation有効) Console.Write("ctxTest_Validating" + "\n");}
    (追記)
    上記のコードのように、フラグを使用して制御した方が、コードは読みやすくなると思います。テキストボックス1_Validatingイベントハンドラが状況によって実行されたりされなかったりするのは、テキストボックス1_Validatingイベントハンドラのコードを見ただけではわかりませんから、後日、わかりにくいコードになる可能性があります。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/



    2013年1月31日 7:22
    モデレータ
  • ご返答ありがとうございます。

    フラグ管理ですが trapemiyaさんのおっしゃるとおり、

    後日わかりにくくなるため避けたいです。

    やはり簡単にはいかないですよね・・・・。
    2013年2月1日 0:22
  • あとはフラグを使わない方法ですと、発想を変えて移動前のコントロールのCausesValidationをfalseにしてしまう方法でしょうか・・・

    ButtonクリックでActiveControlが変わらないように、Buttonクラスを継承したNotSelectableButtonクラスというボタンを用意します。

    public class NotSelectableButton :  Button
    {
        public NotSelectableButton() : base()
        {
            this.SetStyle(ControlStyles.Selectable, false);
        }
    }

    一度コンパイルするとツールボックスに表示されますので、それをデザイナでフォームにドラッグして使用して下さい。
    あとはこのボタンのクリックイベントハンドラで以下のようにするだけです。

    private void notSelectableButton1_Click(object sender, EventArgs e)
    {
        Control ctr = this.ActiveControl;
        ctr.CausesValidation = false;
    
        ctxTest.Select();
        
        ctr.CausesValidation = true;
    }


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    • 回答の候補に設定 佐伯玲 2013年2月12日 5:58
    2013年2月1日 2:00
    モデレータ
  •  逆に、全てのコントロールで CausesValidating を false にしておいて、ContainerControl.Validate メソッドを使って、コードから Validate を実行させれば良いのでは?

     あれ?CausesValidation を false にしたコントロールについては Validating イベントが発生しない??(VS2010)そんな仕様だったかなぁ?

    Control.CausesValidation プロパティ(.NET framework 3.5)
    そのコントロールが原因で、フォーカスを受け取ると検証が必要なコントロールに対して、検証が実行されるかどうかを示す値を取得または設定します。

     「そのコントロール」の検証については、影響しないように思うけどなぁ...


     1→4と移動したときは検証する。1→2→3→ボタン→4と移動したときは検証しない。
    では、1→ボタン→4の時は?1→2→ボタン→4の時は?1→2→4の時は?


     移動を指示した後に設定しても遅いので、移動した後に「ここから移動する時にはどうか」を設定するとか。

    // TextBox を4つ、Button を1つ配置。
    // TextBox の Validating イベントを textBox_Validating に設定。
    // TextBox の Enter イベントを textBox_Enter に設定。
    // textBox2, textBox3, button1 の CausesValidation を false に設定。
    // button1 の Click イベントを button1_click に設定。
    namespace WindowsFormsApplication1
    {
    	public partial class Form1 : Form
    	{
    		public Form1()
    		{
    			InitializeComponent();
    		}
    
    		private void textBox_Validating(object sender, CancelEventArgs e)
    		{
    			Control s = sender as Control;
    			if (s != null)
    			{
    				Debug.WriteLine("{0} Validating", s.Name);
    			}
    		}
    
    		private void textBox_Enter(object sender, EventArgs e)
    		{
    			Control c = sender as TextBox;
    			// textBox4 以外がフォーカスを受け取ったときに、
    			// そのコントロールの CausesValidation と同じ値を
    			// textBox4.CausesValidation に設定する。
    			if (c != null && c != this.textBox4)
    			{
    				this.textBox4.CausesValidation = c.CausesValidation;
    			}
    		}
    
    		private void button1_Click(object sender, EventArgs e)
    		{
    			this.textBox4.Select();
    		}
    	}
    }
    


    Jitta@わんくま同盟

    • 回答の候補に設定 佐伯玲 2013年2月12日 5:58
    2013年2月1日 12:03
  •  trapemiyaさんへ

    ご返答ありがとうございます。

    ボタンをカスタムすることは考えていなかったんで

    検討させていただきます!!


    • 編集済み soil3 2013年2月4日 1:35
    2013年2月4日 0:33
  • Jittaさんへ

    ご返答ありがとうございます。

    移動後にCausesValidationを変更しているのが

    問題だと感じているので、タイミングについて

    検証してみます。

    2013年2月4日 9:05